home *** CD-ROM | disk | FTP | other *** search
/ Super PC 31 / Super PC 31 (Shareware).iso / spc / inter / speakf / fuente / frame.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-30  |  92.4 KB  |  2,820 lines

  1. /*
  2.  
  3.                     MDI Frame window handler
  4.                     
  5. */
  6.                     
  7. #include "netfone.h"
  8.  
  9. //    Variables exported
  10.  
  11. HWAVEOUT hWaveOut = NULL;                // Wave output handle
  12. HWAVEIN hWaveIn = NULL;                 // Wave input handle
  13. int outputActive = FALSE;                // Is wave output open ?
  14. int inputActive = FALSE;                // Is wave input open ?
  15. int openConnections = 0;                // Number of open connections
  16. int listeners = 0;                        // Current wantsInput windows
  17. int broadcasting = FALSE;                // Broadcasting to all connections ?
  18. static int outputTimeout = 0;            // Output release timeout
  19. long outputPending = 0;                 // Output buffers in queue
  20. int halfDuplexTransition = FALSE;        // Transitioning from output to input ?
  21. int outputInShutdown = FALSE;            // Close output when last buffer returned
  22. gsm gsmh = NULL;                        // GSM handle
  23. HCURSOR phoneCursor = NULL,
  24.         earCursor = NULL;                // Cursors
  25. int compression = FALSE;                // 2X compression mode
  26. int gsmcompress = TRUE;                // GSM compression mode
  27. int adpcmcompress = FALSE;                // ADPCM compression mode
  28. int lpccompress = FALSE;                // LPC compression mode
  29.  
  30. int modemEnable = FALSE;                // Modem connections enabled ?
  31. char modemInitString[128] =                // Modem initialisation string
  32.                             "ATQ0V1E1S0=1";
  33. char baudrate[12] = "19200";            // Baud rate
  34. char commport[12] = "COM1";                // Communications port
  35. int modemShowRant = TRUE;                // Show rant about Windows serial I/O support
  36. int modemSessions = 0;                    // Open sessions on modem
  37.  
  38. int alwaysBindSocket = FALSE;            // Bind output socket (WINSOCK bug work-around)
  39. int useSendNotSendto = FALSE;            // Use send() to socket, not sendto()
  40. int waNetNoConnect = FALSE;                // Don't connect(), use sendto()
  41. int waNetUseSend = FALSE;                // Use send(), not sendto() always
  42. int waNetMultiTTLisChar = FALSE;        // Argument to IP_MULTICAST_TTL setsockopt is char
  43. int waAudioHalf = FALSE;                // Assume audio half-duplex; don't test
  44. int waAudio11025 = FALSE;                // Assume audio 11025 samples/sec                                                
  45.  
  46. int halfDuplex = FALSE;                    // Audio hardware is half-duplex
  47. static int audioIs8Bit = FALSE;            // Nonzero if audio is 8 bit only
  48. static int audioUse8Bit = FALSE;        // Use 8 bit audio
  49. int netMaxSamples;                        // Maximum samples network can send
  50. static int currentInputLength;            // Current expected input buffer length
  51. int currentInputSamples;                // Samples desired in current input buffers
  52. int holped = FALSE;                        // Help was invoked somewhere
  53. static char ringFileName[MAX_PATH] = "";// Ring file name
  54. static int lookWho_sTalking = FALSE;    // Restore from icon on new connection
  55.  
  56. char lwl_s_server[MAX_PATH];            // Look Who's Listening server name
  57. char lwl_s_email[80];                    //        E-mail address
  58. char lwl_s_fullname[80];                //        Full name
  59. char lwl_s_phone[80];                    //        Telephone number
  60. char lwl_s_location[80];                //        Location name
  61. int lwl_s_publish;                        //        Publish in directory ?
  62. int lwl_s_exact;                        //        Exact match only ?
  63.  
  64. char lwl_a_server[MAX_PATH];            // Look Who's Listening query server
  65. int lwl_a_exact;                        //        Exact matches only
  66.  
  67. int lwl_t_published = FALSE;            // Directory listing published
  68. int lwl_t_resend = 0;                    // Time to update directory listing ?
  69. int lwl_t_diactive = FALSE;                // LWL dialogue up
  70.  
  71. unsigned long ssrc;                          // RTP synchronisation source identifier
  72. unsigned long timestamp;                   // RTP packet timestamp
  73. unsigned short seq;                          // RTP packet sequence number
  74. char *sdes = NULL;                          // RTP SDES packet
  75. int sdesl;                                  // RTP SDES packet length    
  76.  
  77. HINSTANCE hInst;                        // The current instance handle
  78. HACCEL hAccel;                            // Accelerator table handle
  79. HWND hwndMDIFrame;                        // MDI frame  window handle
  80. HWND hwndMDIClient;                        // MDI client window handle
  81. HWND hDlgPropeller = NULL;                // Propeller head modeless dialogue
  82. FARPROC pfnPropeller = NULL;            // Propeller head procedure instance
  83. LPSTR commandLine = NULL;                // Command line from invocation
  84. HWND hDlgAnswer = NULL;                    // Answering machine modeless dialogue
  85. FARPROC pfnAnswer = NULL;                // Answering machine procedure instance
  86. LPSTR pszAppName;                        // Application name
  87. INT    tmAveCharWidth;                        // TEXTMETRIC.tmAveCharWidth
  88. INT    tmHeight;                            // TEXTMETRIC.tmHeight
  89.  
  90. int rememberedConnections = 0;            // Number of remembered connections
  91. LPSTR rememberedConnection[REMEMBER_CONNECTIONS];  // Remembered connections
  92.  
  93. UINT fileOpenHelpButton;                // File open help button message value
  94. char *fileHelpKey = NULL;                // Help key for file open/save in progress
  95.  
  96. int multiMemberships = 0;                // Number of multicast group memberships
  97. struct in_addr multiAddr[IP_MAX_MEMBERSHIPS]; // Multicast group IP numbers
  98. LPSTR multiName[IP_MAX_MEMBERSHIPS];    // Multicast group names
  99. int multiLoop = FALSE;                    // Multicast loop-back mode
  100. int multiBrainDead = FALSE;                // Multicast loop-back option not supported
  101.  
  102. //    Local variables
  103.  
  104. #define nWaveHeaders    4
  105. static SOCKET sCommand = INVALID_SOCKET;    // Command socket
  106. static SOCKET lwlsock = INVALID_SOCKET;    // Look Who's Listening periodic update socket
  107. static LPWAVEHDR inWaveHeader[nWaveHeaders];// Pointers to wave input buffers
  108. static int inputTerm = FALSE;            // Input terminating
  109. static soundbuf receivedSoundBuffer;    // Sound buffer from network
  110. static LPSTR modemSb = NULL;            // Modem sound buffer assembly area
  111.  
  112. static int audioChannels = 1;            // Audio input channels
  113. static int samplesPerSecond = 8000;        // Sound sampling rate
  114. static int bytesPerSecond = 16000;        // Sample bytes per second
  115. static int sampleAlignment = 2;            // Sample frame size
  116. static int bitsPerSample = 16;            // Bits per sample
  117.  
  118. static char kS0[] = "0", kS1[] = "1";    // Frequently used string constants
  119.  
  120. //    Export current audio settings to About dialogue
  121.  
  122. int aboutInSamples = 0;                    // Input samples per second
  123. int aboutInBits;                        // Input bits per sample
  124. int aboutOutSamples = 0;                // Output samples per second
  125. int aboutOutBits;                        // Output bits per sample
  126.  
  127. //    Export various status information to Propeller Head dialogue
  128.  
  129. long packetsReceived = 0,                // Network packet traffic counters
  130.      packetsSent = 0,
  131.      inputPacketsLost = 0,                // Input packets lost due to half-duplex
  132.      outputPacketsLost = 0;                // Output packets lost due to net traffic
  133.      
  134. int messageQueueSize = 120;                // Inbound message queue size
  135.  
  136. //    Network properties
  137.  
  138. int aboutUDPmax = 0;                    // Maximum UDP packet size
  139.  
  140. //  Private constants
  141.  
  142. #define DESIRED_WINSOCK_VERSION 0x0101  // we'd like winsock ver 1.1...
  143. #define MINIMUM_WINSOCK_VERSION 0x0001  // ...but we'll take ver 1.0
  144.  
  145. #define MODEM_INPUT_EVENT    9999        // Modem input socket status
  146.  
  147. //    MDI message forwarder
  148.  
  149. static LRESULT Frame_MDIMessageForwarder(HWND hwnd, UINT nMessage,
  150.                                    WPARAM wParam, LPARAM lParam)
  151. {
  152.     return DefFrameProc(hwnd, hwndMDIClient, nMessage, wParam, lParam);
  153.  
  154. }
  155.  
  156. // PROPELLER  --  Update propeller head dialogue if it's displayed.
  157.  
  158. void propeller(int control, DWORD value)
  159. {
  160.     if (hDlgPropeller != NULL) {
  161.         char s[80];
  162.         
  163.         wsprintf(s, Format(0), value);
  164.         SetDlgItemText(hDlgPropeller, control, s);
  165.     }
  166. }
  167.                                       
  168. /* FINDCLIENTBYHOST  --  Find the connection window for a given
  169.                          host address. */                                      
  170.                                       
  171. static HWND findClientByHost(LPSOCKADDR_IN paddr)
  172. {
  173.     HWND hwnd;
  174.  
  175.     hwnd = GetWindow(hwndMDIClient, GW_CHILD);
  176.  
  177.     while (hwnd != NULL) {
  178.         if ((WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC) == ((WNDPROC) connectWndProc)) {
  179.             LPCLIENT_DATA pClientData = CLIENTPTR(hwnd);
  180.     
  181.             if ((pClientData != NULL) &&
  182.                 (pClientData->inetSock.sin_addr.s_addr == paddr->sin_addr.s_addr)) {
  183.                 return hwnd;
  184.             }
  185.         }
  186.         hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  187.     }
  188.     return NULL;
  189. }
  190.  
  191. /*  GETACTIVECONNECTION  --  Get the active connection window.  If no
  192.                              connection is active, NULL is returned.
  193.                              A connection will not be returned unless
  194.                              it has a valid client data structure.  */
  195.                              
  196. static HWND getActiveConnection(void)
  197. {
  198.     LRESULT mdiActive;
  199.     HWND hwnd;
  200.  
  201.     if (hwndMDIClient != NULL) {
  202.         mdiActive = SendMessage(hwndMDIClient, WM_MDIGETACTIVE,
  203.                                 0, 0L);
  204.         hwnd = (HWND) LOWORD(mdiActive); 
  205.         if (hwnd != NULL &&
  206.             ((WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC) == ((WNDPROC) connectWndProc)) &&
  207.             CLIENTPTR(hwnd) != NULL) { 
  208.             return hwnd;
  209.         }
  210.     }
  211.     return NULL;                            
  212. }
  213.  
  214. /*  MULTICASTJOIN  --  Join or drop multicast groups given in
  215.                        the multicast table. */
  216.  
  217. void multicastJoin(HWND hwnd, int join)
  218. {
  219.     int i, what;
  220.     struct ip_mreq mreq;
  221.     
  222.     if (join) {
  223.         int sstat;
  224.         if (waNetMultiTTLisChar) {
  225.             u_char loop;
  226.             
  227.             loop = (u_char) multiLoop;
  228.             sstat = setsockopt(sCommand, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop,
  229.                     sizeof loop);
  230.         } else {
  231.             int loop;
  232.             
  233.             loop = (int) multiLoop;
  234.             sstat = setsockopt(sCommand, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &loop,
  235.                     sizeof loop);
  236.         }
  237.         if (sstat == -1) {
  238.             int serr = WSAGetLastError();
  239.             
  240.             if (serr == WSAENOPROTOOPT) {
  241.                 //    Windows 95 and NT don't support IP_MULTICAST_LOOP
  242.                 multiBrainDead = TRUE;
  243.             } else {
  244.                 /*    Since many Winsock implementations don't support multicast,
  245.                     disable the warning to prevent a natter every time we start
  246.                     up.  */ 
  247. #ifdef MULTICAST_WARNING                        
  248.                 MsgBox(hwnd, MB_ICONSTOP | MB_OK,
  249.                         Format(8),
  250.                         serr, SockerrToString(serr));
  251. #endif                        
  252.             }
  253.         }
  254.     }
  255.     what = join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 
  256.     mreq.imr_interface.s_addr = htonl(INADDR_ANY);
  257.     for (i = 0; i < multiMemberships; i++) {
  258.         mreq.imr_multiaddr.s_addr = multiAddr[i].s_addr;
  259.         if (setsockopt(sCommand, IPPROTO_IP, what,
  260.                 (char *) &mreq, sizeof mreq) == -1) {
  261.             int serr = WSAGetLastError();
  262.                 
  263.             MsgBox(hwnd, MB_ICONSTOP | MB_OK,
  264.                     Format(9),
  265.                     (LPSTR) (join ? Format(10) : Format(11)), inet_ntoa(multiAddr[i]),
  266.                     serr, SockerrToString(serr));
  267.         }
  268.         if (!what && multiName[i] != NULL) {
  269.             GlobalFreePtr(multiName[i]);
  270.             multiName[i] = NULL;
  271.         }    
  272.     }
  273. }
  274.  
  275. /*  ISHALFDUPLEX  --  Try to open audio input and output simultaneously.
  276.                       If it fails, mark the hardware half-duplex.  */
  277.                       
  278. static int isHalfDuplex(HWND hwnd)
  279. {
  280.     LPPCMWAVEFORMAT wf = NULL;    
  281.     WORD woo;
  282.     int hdx = FALSE;
  283.     
  284.     if (waAudio11025) {
  285.         samplesPerSecond = 11025;
  286.         bytesPerSecond = samplesPerSecond * 2;
  287.     }
  288.         
  289.     wf = (LPPCMWAVEFORMAT) GlobalAllocPtr(GPTR, sizeof(PCMWAVEFORMAT));
  290.     if (wf == NULL) {
  291.         MessageBox(hwnd, rstring(IDS_T_WAVE_IN_FORMAT_ERR),
  292.                NULL, MB_OK | MB_ICONEXCLAMATION);
  293.            return -1;
  294.     }
  295.     wf->wf.wFormatTag = WAVE_FORMAT_PCM;
  296.     wf->wf.nChannels = audioChannels;
  297.     
  298.     //    Input initialisation
  299.         
  300.     while (TRUE) {
  301.         wf->wf.nSamplesPerSec = samplesPerSecond;
  302.         wf->wf.nAvgBytesPerSec = bytesPerSecond;
  303.         wf->wf.nBlockAlign = sampleAlignment;
  304.         wf->wBitsPerSample = bitsPerSample;
  305.          woo = waveInOpen(&hWaveIn, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  306.                  0L, 0L, WAVE_FORMAT_QUERY);
  307.          if (bitsPerSample > 8 && woo == WAVERR_BADFORMAT) {
  308.              audioIs8Bit = TRUE;
  309.          } 
  310.                      
  311.          /* If our preferred mode (16 bit) isn't supported, try falling
  312.             back to bottom-feeder 8 bit per sample mode. */            
  313.     
  314.          if ((audioUse8Bit || woo == WAVERR_BADFORMAT) && (bitsPerSample > 8)) {
  315.              bitsPerSample /= 2;
  316.              sampleAlignment /= 2;
  317.              bytesPerSecond /= 2;    
  318.             wf->wf.nAvgBytesPerSec = bytesPerSecond;
  319.             wf->wf.nBlockAlign = sampleAlignment;
  320.             wf->wBitsPerSample = bitsPerSample;
  321.              woo = waveInOpen(&hWaveIn, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  322.                      0L, 0L, WAVE_FORMAT_QUERY);
  323.          }
  324.              
  325.          /* If we've failed to initialise in either 16 or 8 bit mode
  326.             at 8000 samples per second, it's possible the sound card
  327.             doesn't support any sampling mode below the Windows standard
  328.             of 11025 samples per second.  Have another go-round and see
  329.             if 11025 works. */
  330.              
  331.          if (woo == WAVERR_BADFORMAT && samplesPerSecond == 8000) {
  332.              samplesPerSecond = 11025;
  333.              bitsPerSample = 16;
  334.              sampleAlignment = bitsPerSample / 8;
  335.              bytesPerSecond = samplesPerSecond * sampleAlignment;  
  336.          } else {
  337.              break;
  338.          }
  339.      } 
  340.     if (woo != 0) {
  341.         MessageBox(hwnd, rstring(IDS_T_WAVE_RECORD_FORMAT_ERR),
  342.                NULL, MB_OK | MB_ICONEXCLAMATION);
  343.         GlobalFreePtr(wf);
  344.         return -1;
  345.     }
  346.     if ((woo = waveInOpen(&hWaveIn, (UINT) WAVE_MAPPER,
  347.           (LPWAVEFORMAT) wf, (DWORD) (UINT) hwnd, 0L, (DWORD) CALLBACK_WINDOW)) != 0) {
  348.         char et[MAXERRORLENGTH];
  349.                 
  350.         waveInGetErrorText(woo, et, sizeof et);
  351.         MessageBox(hwnd, et, rstring(IDS_T_ERR_OPEN_WAVE_INPUT), MB_OK | MB_ICONEXCLAMATION);
  352.         GlobalFreePtr(wf);
  353.         return -1;
  354.     }
  355.     aboutInBits = bitsPerSample;
  356.     aboutInSamples = samplesPerSecond;
  357.     
  358.     /* If workaround is set, close input now and mark half-duplex
  359.        without waiting for the open of output to fail. */
  360.     
  361.     if (waAudioHalf) {
  362.         waveInClose(hWaveIn);
  363.         hdx = halfDuplex = TRUE;
  364.     }
  365.     
  366.     //    Output initialisation
  367.  
  368.     while (TRUE) {
  369.          woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  370.                                  0L, 0L, WAVE_FORMAT_QUERY);
  371.          if (bitsPerSample > 8 && woo == WAVERR_BADFORMAT) {
  372.              audioIs8Bit = TRUE;
  373.          } 
  374.                      
  375.          /* If our preferred mode (16 bit, 11025 samples/second) isn't
  376.             supported, try falling back to bottom-feeder 8 bit per sample
  377.             mode. */            
  378.     
  379.          if ((audioUse8Bit || woo == WAVERR_BADFORMAT) && bitsPerSample > 8) {
  380.              bitsPerSample /= 2;
  381.              sampleAlignment /= 2;
  382.              bytesPerSecond /= 2;    
  383.             wf->wf.nAvgBytesPerSec = bytesPerSecond;
  384.             wf->wf.nBlockAlign = sampleAlignment;
  385.             wf->wBitsPerSample = bitsPerSample;
  386.              woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  387.                      0L, 0L, WAVE_FORMAT_QUERY);
  388.          }
  389.              
  390.          /* If we've failed to initialise in either 16 or 8 bit mode
  391.             at 8000 samples per second, it's possible the sound card
  392.             doesn't support any sampling mode below the Windows standard
  393.             of 11025 samples per second.  Have another go-round and see
  394.             if 11025 works. */
  395.              
  396.          if (woo == WAVERR_BADFORMAT && samplesPerSecond == 8000) {
  397.              samplesPerSecond = 11025;
  398.              bitsPerSample = 16;
  399.              sampleAlignment = bitsPerSample / 8;
  400.              bytesPerSecond = samplesPerSecond * sampleAlignment;  
  401.          } else {
  402.              break;
  403.          }
  404.      } 
  405.          
  406.     if (woo != 0) {                
  407.         char et[MAXERRORLENGTH];
  408.             
  409.         waveOutGetErrorText(woo, et, sizeof et);
  410.         MessageBox(hwnd, et, rstring(IDS_T_WAVE_PLAY_FORMAT_ERR),
  411.                MB_OK | MB_ICONEXCLAMATION);
  412.         GlobalFreePtr(wf);
  413.         hdx = -1;
  414.         goto FatalAudioExit;
  415.     }
  416.     if ((woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER,
  417.           (LPWAVEFORMAT) wf, (DWORD) (UINT) hwnd, 0, (DWORD) CALLBACK_WINDOW)) != 0) {
  418.         char et[MAXERRORLENGTH];
  419.             
  420.         /* The next line looks wrong, doesn't it?  But I've seen drivers
  421.            for half-duplex sound boards that return NOTSUPPORTED instead
  422.            of ALLOCATED when you try to open input and output at the
  423.            same time. */
  424.             
  425.         if (!waAudioHalf && (woo == MMSYSERR_ALLOCATED || woo == MMSYSERR_NOTSUPPORTED)) {
  426.             hdx = halfDuplex = TRUE;
  427.             waveInClose(hWaveIn);
  428.             if (waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER,
  429.                       (LPWAVEFORMAT) wf, (DWORD) (UINT) hwnd, 0,
  430.                       (DWORD) CALLBACK_WINDOW) == 0) {
  431.                 aboutOutBits = bitsPerSample;
  432.                 aboutOutSamples = samplesPerSecond;
  433.                 waveOutClose(hWaveOut);
  434.                 goto HdxAudioExit;                                  
  435.               }
  436.         } else {    
  437.             waveOutGetErrorText(woo, et, sizeof et);
  438.             MessageBox(hwnd, et, rstring(IDS_T_ERR_OPEN_WAVE_OUTPUT),
  439.                 MB_OK | MB_ICONEXCLAMATION);
  440.             hdx = -1;
  441.         }
  442.     } else {
  443.         aboutOutBits = bitsPerSample;
  444.         aboutOutSamples = samplesPerSecond;
  445.         waveOutClose(hWaveOut);
  446.     }
  447.     
  448. FatalAudioExit:
  449.     if (!waAudioHalf) {
  450.         waveInClose(hWaveIn);
  451.     }
  452. HdxAudioExit:
  453.     GlobalFreePtr(wf);
  454.     return hdx;
  455. }                      
  456.  
  457. /*    WAVEOUTSHUTDOWN  --  Shutdown wave audio output, if open.  */
  458.                          
  459. static void waveOutShutdown(void)
  460. {
  461.     if (outputActive) {
  462.         V waveOutReset(hWaveOut);
  463.         if (outputPending) {
  464.             outputInShutdown = TRUE;
  465.             propUpdateAudio();
  466.         } else {
  467.             V waveOutClose(hWaveOut);
  468.             outputActive = FALSE;
  469.             propUpdateAudio();
  470.         }
  471.     }
  472. }
  473. /*    INPUTSAMPLECOUNT  --  Calculate best sample count for an audio input
  474.                           buffer based on the current compression
  475.                           modes and the characteristics of the audio
  476.                           hardware.  */
  477.                            
  478. int inputSampleCount(void)
  479. {
  480.     int l = lpccompress ? (compression ? 3600 : 1800) : 
  481.                 (gsmcompress ?
  482.                     (compression ? 3200 : 1600) :
  483.                     ((512 - (sizeof(soundbuf) - BUFL)) * (compression ? 2 : 1)));
  484.     if (adpcmcompress) {
  485.         l *= 2;
  486.         l -= 4;                          // Leave room for state at the end
  487.     }
  488.     return l;
  489. }                                                                               
  490.  
  491. /*    INPUTBUFFERLENGTH  --  Calculate best length in bytes for an audio input
  492.                            buffer based on the current compression
  493.                            modes and the characteristics of the audio
  494.                            hardware.  */
  495.                            
  496. static int inputBufferLength(void)
  497. {
  498.     return (bitsPerSample / 8) * inputSampleCount();
  499. }                                                                               
  500.  
  501. /*    STARTWAVEINPUT  --  Activate wave audio input.  Returns TRUE
  502.                         if successful, FALSE if no input will be
  503.                         forthcoming for some twiddley reason or
  504.                         another.  The window handle is simply used
  505.                         as the parent for the message boxes announcing
  506.                         the bad news.  */
  507.  
  508. int startWaveInput(HWND hwnd)
  509. {
  510.     //    Attempt to initialise the audio input port
  511.     
  512.     if (!inputActive) {
  513.         int i;
  514.         LPPCMWAVEFORMAT wf;    
  515.         WORD woo;
  516.         
  517.         wf = (LPPCMWAVEFORMAT) GlobalAllocPtr(GPTR, sizeof(PCMWAVEFORMAT));
  518.         if (wf == NULL) {
  519.             MessageBox(hwnd, rstring(IDS_T_WAVE_IN_FORMAT_ERR),
  520.                    NULL, MB_OK | MB_ICONEXCLAMATION);
  521.         }
  522.         wf->wf.wFormatTag = WAVE_FORMAT_PCM;
  523.         wf->wf.nChannels = audioChannels;
  524.         
  525.         while (TRUE) {
  526.             wf->wf.nSamplesPerSec = samplesPerSecond;
  527.             wf->wf.nAvgBytesPerSec = bytesPerSecond;
  528.             wf->wf.nBlockAlign = sampleAlignment;
  529.             wf->wBitsPerSample = bitsPerSample;
  530.              woo = waveInOpen(&hWaveIn, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  531.                      0L, 0L, WAVE_FORMAT_QUERY);
  532.              if (bitsPerSample > 8 && woo == WAVERR_BADFORMAT) {
  533.                  audioIs8Bit = TRUE;
  534.              } 
  535.                      
  536.              /* If our preferred mode (16 bit) isn't supported, try falling
  537.                 back to bottom-feeder 8 bit per sample mode. */            
  538.     
  539.              if ((audioUse8Bit || woo == WAVERR_BADFORMAT) && (bitsPerSample > 8)) {
  540.                  bitsPerSample /= 2;
  541.                  sampleAlignment /= 2;
  542.                  bytesPerSecond /= 2;    
  543.                 wf->wf.nAvgBytesPerSec = bytesPerSecond;
  544.                 wf->wf.nBlockAlign = sampleAlignment;
  545.                 wf->wBitsPerSample = bitsPerSample;
  546.                  woo = waveInOpen(&hWaveIn, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  547.                          0L, 0L, WAVE_FORMAT_QUERY);
  548.              }
  549.              
  550.              /* If we've failed to initialise in either 16 or 8 bit mode
  551.                 at 8000 samples per second, it's possible the sound card
  552.                 doesn't support any sampling mode below the Windows standard
  553.                 of 11025 samples per second.  Have another go-round and see
  554.                 if 11025 works. */
  555.              
  556.              if (woo == WAVERR_BADFORMAT && samplesPerSecond == 8000) {
  557.                  samplesPerSecond = 11025;
  558.                  bitsPerSample = 16;
  559.                  sampleAlignment = bitsPerSample / 8;
  560.                  bytesPerSecond = samplesPerSecond * sampleAlignment;  
  561.              } else {
  562.                  break;
  563.              }
  564.          } 
  565.         if (woo != 0) {
  566.             MessageBox(hwnd, rstring(IDS_T_WAVE_RECORD_FORMAT_ERR),
  567.                    NULL, MB_OK | MB_ICONEXCLAMATION);
  568.             GlobalFreePtr(wf);
  569.             return FALSE;
  570.         }
  571.         for (i = 0; i < 2; i++) {
  572.             if ((woo = waveInOpen(&hWaveIn, (UINT) WAVE_MAPPER,
  573.                   (LPWAVEFORMAT) wf, (DWORD) (UINT) hwndMDIFrame, 0L, (DWORD) CALLBACK_WINDOW)) != 0) {
  574.                 char et[MAXERRORLENGTH];
  575.                 
  576.             
  577.                 /* The next line looks wrong, doesn't it?  But I've seen drivers
  578.                    for half-duplex sound boards that return NOTSUPPORTED instead
  579.                    of ALLOCATED when you try to open input and output at the
  580.                    same time. */
  581.             
  582.                 if (i == 0 && outputActive && (woo == MMSYSERR_ALLOCATED || 
  583.                         woo == MMSYSERR_NOTSUPPORTED)) {
  584.                 
  585.                     /* Okay, the preponderance of evidence points at our
  586.                        machine being burdened with a half-duplex audio
  587.                        device--one where the fact that we're playing audio
  588.                        prevents from from simultaneously receiving it.
  589.                        This is bad.  Let's proceed as we'd have done on
  590.                        the 80 metre band in 1935--mute the receiver and
  591.                        blast on our carrier regardless. */
  592.                     
  593.                     V waveOutReset(hWaveOut);
  594.                     halfDuplex = TRUE;            // Indicate wave output swiped
  595.                     if (outputPending) {
  596.                         halfDuplexTransition = TRUE;
  597.                         GlobalFreePtr(wf);
  598.                         propUpdateAudio();
  599.                         return TRUE;
  600.                     } else {
  601.                         V waveOutClose(hWaveOut);
  602.                         outputActive = FALSE;
  603.                         propUpdateAudio();
  604.                     }
  605.                     continue;   
  606.                 }    
  607.                 waveInGetErrorText(woo, et, sizeof et);
  608.                 MessageBox(hwnd, et,
  609.                        rstring(IDS_T_ERR_OPEN_WAVE_INPUT), MB_OK | MB_ICONEXCLAMATION);
  610.                 GlobalFreePtr(wf);
  611.                 return FALSE;
  612.             }
  613.             break;
  614.         }
  615.         GlobalFreePtr(wf);
  616.         
  617.         /*    Now allocate and prepare the sound input buffers.  Don't
  618.             you just love the code vomit Windows' inability to free
  619.             resources when a program terminates creates?  */
  620.         
  621.         currentInputLength = inputBufferLength();
  622.         currentInputSamples = inputSampleCount();
  623.         for (i = 0; i < nWaveHeaders; i++) {
  624.             inWaveHeader[i] = NULL;
  625.         }
  626.         for (i = 0; i < nWaveHeaders; i++) {
  627.             inWaveHeader[i] = (LPWAVEHDR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  628.                                                 sizeof(WAVEHDR));
  629.             if (inWaveHeader[i] == NULL) {
  630.                 int j;
  631.                 
  632.                 for (j = i - 1; j >= 0; j++) {
  633.                     waveInUnprepareHeader(hWaveIn, inWaveHeader[j], sizeof(WAVEHDR));
  634.                     GlobalFreePtr(inWaveHeader[j]->lpData);
  635.                     GlobalFreePtr(inWaveHeader[j]);
  636.                     inWaveHeader[j] = NULL;
  637.                 }
  638.                 MessageBox(hwnd, rstring(IDS_T_INPUT_HEADER_ERR),
  639.                        NULL, MB_OK | MB_ICONEXCLAMATION);
  640.                    waveInClose(hWaveIn);
  641.                 return FALSE;
  642.             }
  643.             
  644.             /*    Since the user is allowed to change the sample size
  645.                 and compression modes on the fly, but the audio input
  646.                 buffers are used for the entire run of the program, we
  647.                 allocate them for the worst-case scenario: both LPC and
  648.                 2X compression enabled with 16 bit samples.  We've already
  649.                 established whether the hardware can run at 8000 samples
  650.                 per second, so only allocate the larger buffer needed for
  651.                 11025 sample per second hardware if it's actually required. */
  652.                 
  653.             inWaveHeader[i]->lpData = (LPSTR) GlobalAllocPtr(GMEM_MOVEABLE | GMEM_SHARE,
  654.                 (DWORD) ((samplesPerSecond == 8000) ? 7200 : 10000));
  655.             if (inWaveHeader[i]->lpData == NULL) {
  656.                 int j;
  657.                 
  658.                 GlobalFreePtr(inWaveHeader[i]);
  659.                 inWaveHeader[i] = 0;
  660.                 for (j = i - 1; j >= 0; j++) {
  661.                     waveInUnprepareHeader(hWaveIn, inWaveHeader[j], sizeof(WAVEHDR));
  662.                     GlobalFreePtr(inWaveHeader[j]->lpData);
  663.                     GlobalFreePtr(inWaveHeader[j]);
  664.                     inWaveHeader[j] = NULL;
  665.                 }
  666.  
  667.                 MessageBox(hwnd, rstring(IDS_T_INPUT_BUFFER_ERR),
  668.                        NULL, MB_OK | MB_ICONEXCLAMATION);
  669.                    waveInClose(hWaveIn);
  670.                 return FALSE;
  671.             }
  672.             
  673.             inWaveHeader[i]->dwBufferLength = currentInputLength;
  674.             inWaveHeader[i]->dwFlags = 0;
  675.             waveInPrepareHeader(hWaveIn, inWaveHeader[i], sizeof(WAVEHDR));
  676.         }
  677.         
  678.         for (i = 0; i < nWaveHeaders; i++) {
  679.             waveInAddBuffer(hWaveIn, inWaveHeader[i], sizeof(WAVEHDR)); 
  680.         }
  681.         inputTerm = FALSE;
  682.         waveInStart(hWaveIn);
  683.         inputActive = TRUE;
  684.         aboutInSamples = samplesPerSecond;
  685.         aboutInBits = bitsPerSample;
  686.         propUpdateAudio();
  687.     }
  688.     return TRUE;
  689. }
  690.                                       
  691. /*  TERMINATEWAVEINPUT  --  Shut down wave input and release all
  692.                             resources associated with it.  */
  693.                             
  694. void terminateWaveInput(void)
  695. {
  696.     if (inputActive) {
  697.         int i;
  698.         
  699.         inputTerm = TRUE;
  700.         waveInReset(hWaveIn);
  701.         for (i = 0; i < nWaveHeaders; i++) {
  702.             if (inWaveHeader[i] != NULL) {
  703.                 waveInUnprepareHeader(hWaveIn, inWaveHeader[i], sizeof(WAVEHDR));
  704.                 if (inWaveHeader[i]->lpData != NULL) {
  705.                     GlobalFreePtr(inWaveHeader[i]->lpData);
  706.                 }
  707.                 GlobalFreePtr(inWaveHeader[i]);
  708.                 inWaveHeader[i] = NULL;
  709.             }
  710.         }
  711.         waveInClose(hWaveIn);
  712.         hWaveIn = NULL;
  713.         inputActive = FALSE;
  714.         propUpdateAudio();
  715.     }
  716. }
  717.  
  718. /*    REMEMBERNEWCONNECTION  --  Add a connection file name to the list
  719.                                of remembered connections.  If it's
  720.                                already in the list, promote it to the
  721.                                first item.  */
  722.                                
  723. static void rememberNewConnection(LPSTR connectionFile)
  724. {
  725.     int i;
  726.     LPSTR hostSave;
  727.             
  728.     /*    See if the connection file already appears in the list
  729.         of recent connections.  If so, delete it from the list.  */
  730.             
  731.     hostSave = GlobalAllocPtr(GPTR, _fstrlen(connectionFile) + 1);
  732.     _fstrcpy(hostSave, connectionFile);
  733.             
  734.     /*    Careful!  Be sure not to reference connectionFile beyond
  735.         this point, since if we're re-opening a remembered
  736.         connection the following loop may (in fact almost certainly
  737.         will) cause it to be deallocated. */
  738.                  
  739.     if (hostSave != NULL) {
  740.         for (i = 0; i < rememberedConnections; i++) {
  741.             if (_fstricmp(hostSave, rememberedConnection[i]) == 0) {
  742.                 int j;
  743.                     
  744.                 GlobalFreePtr(rememberedConnection[i]);
  745.                 for (j = i + 1; j < rememberedConnections; j++) {
  746.                     rememberedConnection[j - 1] = rememberedConnection[j];    
  747.                 }
  748.                 rememberedConnections--;
  749.             }
  750.         }
  751.                 
  752.         //    Push the list of remembered connections and add this one
  753.                 
  754.         if (rememberedConnections == REMEMBER_CONNECTIONS) {
  755.             GlobalFreePtr(rememberedConnection[REMEMBER_CONNECTIONS - 1]);
  756.             rememberedConnections--;
  757.         }
  758.         for (i = REMEMBER_CONNECTIONS - 1; i >= 1; i--) {
  759.             rememberedConnection[i] = rememberedConnection[i - 1];        
  760.         }
  761.         rememberedConnection[0] = hostSave;
  762.         rememberedConnections++;
  763.     }                
  764. }                               
  765.                                                                   
  766. /*  NEWCONNECTION  --  Initiate a connection to a given named
  767.                        host.  If there's already a connection
  768.                        to this host active, activate its window.  */
  769.                             
  770. VOID newConnection(HWND hwnd, LPSTR connectionFile, LPSTR knownHost)
  771. {
  772.     CHAR szHostName[MAX_HOST];
  773.     SOCKADDR_IN sockHost;
  774. #define addrHost sockHost.sin_addr
  775.     LPCLIENT_DATA pClientData = NULL;
  776.     HWND hwndClient;
  777.     char *cg;
  778.  
  779.     //  Prompt the user for a new host
  780.  
  781.     if (connectionFile == NULL) {
  782.         if (knownHost == NULL) {
  783.             if (!newHostDialogue(hwndMDIFrame, szHostName, &addrHost)) {
  784.                 return;
  785.             }
  786.         } else {
  787.             wsprintf(szHostName, Format(61), knownHost);
  788.             addrHost.s_addr = inet_addr(knownHost);
  789.         }
  790.     } else {
  791.         char inetAddr[64];
  792.         
  793.         cg = rstring(IDS_PF_HOST);
  794.         if (GetPrivateProfileString(cg, rstring(IDS_PF_HOST_NAME), "", szHostName,
  795.                     sizeof szHostName, connectionFile) == 0 ||
  796.             GetPrivateProfileString(cg, rstring(IDS_PF_NETADDR), "", inetAddr,
  797.                     sizeof inetAddr, connectionFile) == 0 ||
  798.             (addrHost.s_addr = inet_addr(inetAddr)) == INADDR_NONE) {
  799.                 MessageBox(hwnd, rstring(IDS_T_CONN_PROFILE_INVALID), connectionFile,
  800.                        MB_OK | MB_ICONEXCLAMATION);
  801.                    return;
  802.         } 
  803.     }
  804.     
  805.     /*    See if there's already a client window communicating with
  806.         this host.  Use equality of IP number as the criterion to
  807.         prevent spoofing due to aliases. */
  808.         
  809.     hwndClient = findClientByHost(&sockHost);
  810.     
  811.     if (hwndClient != NULL) {
  812.         if (IsIconic(hwndClient)) {
  813.             FORWARD_WM_MDIRESTORE(hwndMDIClient, hwndClient, SendMessage);
  814.         }
  815.         BringWindowToTop(hwndClient);
  816.         pClientData = CLIENTPTR(hwndClient);
  817.     } else {
  818.     
  819.         //  Allocate a new connection structure
  820.     
  821.         pClientData = (LPCLIENT_DATA) GlobalAllocPtr(GPTR, sizeof(CLIENT_DATA));
  822.     
  823.         if (pClientData != NULL) {
  824.         
  825.             //  Initialize the connection descriptor
  826.             
  827.             _fmemset(pClientData, 0, sizeof(CLIENT_DATA));
  828.             pClientData->dwType = WINDOW_TYPE_CLIENT;
  829.             pClientData->wantsInput = FALSE;
  830.             pClientData->broadcastBeginTime = GetTickCount();
  831.             pClientData->broadcastEnd = FALSE;
  832.             pClientData->state = Embryonic;
  833.             pClientData->sReply = INVALID_SOCKET;
  834.             pClientData->timeout = -1;        // Connection with local origin is immortal
  835.             pClientData->inetSock.sin_addr.s_addr = addrHost.s_addr;
  836.             pClientData->modemConnection = (addrHost.s_addr == 0);
  837.             pClientData->hFile = HFILE_ERROR;
  838.             pClientData->multicast_scope = 1;
  839.             pClientData->szFile[0] = '\0';
  840.             
  841.             _fstrcpy(pClientData->szHost, szHostName);
  842.         } else {
  843.             return;
  844.         }
  845.     
  846.         //  Create the connection child window
  847.     
  848.         hwndClient = createNewConnection(pClientData);
  849.     
  850.         if (hwndClient == NULL) {
  851.             GlobalFreePtr(pClientData);
  852.             return;
  853.         }
  854.     }        
  855.         
  856.     /*    If the connection was defined in a file, restore the
  857.         parameters to those saved in the file.  */
  858.             
  859.     if (connectionFile != NULL) {
  860.                     
  861.         _fstrcpy(pClientData->connectionFileName, connectionFile); 
  862.         
  863.         cg = rstring(IDS_PF_DEBUG);            
  864.         pClientData->debugging = GetPrivateProfileInt(cg,
  865.             rstring(IDS_PF_DEBUGGING), FALSE, connectionFile);    
  866.         pClientData->loopback = GetPrivateProfileInt(cg,
  867.             rstring(IDS_PF_LOOPBACK), FALSE, connectionFile);
  868.         
  869.         cg = rstring(IDS_PF_MULTICAST);            
  870.         pClientData->multicast_scope = GetPrivateProfileInt(cg,
  871.             rstring(IDS_PF_SCOPE), 1, connectionFile);
  872.         
  873.         cg = rstring(IDS_PF_ENCRYPTION);    
  874.         pClientData->saveKeys = GetPrivateProfileInt(cg,
  875.             rstring(IDS_PF_SAVE_KEYS), FALSE, connectionFile);
  876.         GetPrivateProfileString(cg, rstring(IDS_PF_PGP_USER_NAMES), "",
  877.             pClientData->opgpUserList,
  878.             sizeof pClientData->opgpUserList, connectionFile);            
  879.         if (pClientData->saveKeys) {
  880.             GetPrivateProfileString(cg, rstring(IDS_PF_DES_KEY), "",
  881.                 pClientData->desKeyString,
  882.                 sizeof pClientData->desKeyString, connectionFile);            
  883.             GetPrivateProfileString(cg, rstring(IDS_PF_IDEA_KEY), "",
  884.                 pClientData->ideaKeyString,
  885.                 sizeof pClientData->ideaKeyString, connectionFile);            
  886.             GetPrivateProfileString(cg, rstring(IDS_PF_KEY_FILE), "",
  887.                 pClientData->otpFileName,
  888.                 sizeof pClientData->otpFileName, connectionFile);
  889.         }
  890.         
  891.         //    Initialise settings derived from saved parameters
  892.         
  893.         if (!makeInternalEncryptionKeys(hwndClient, pClientData)) {
  894.             pClientData->otpFileName[0] = 0;    
  895.         }            
  896.             
  897.         /*    Caution: don't reference connectionFile anywhere after
  898.             the call to rememberNewConnection.  If we're re-opening
  899.             a remembered connection, the call may release it.  */
  900.             
  901.         rememberNewConnection(connectionFile);
  902.     }
  903. #undef addrHost    
  904. }
  905.  
  906. /*    PGPSETSESSIONKEY  --  If needed, invoke PGP to decrypt the session
  907.                           key and set it in effect for PGP encrypted
  908.                           packets.  */
  909.                           
  910. static void pgpSetSessionKey(HWND hwnd, LPCLIENT_DATA conn, soundbuf *d)
  911. {
  912.     char cmd[MAX_PATH + 40], kmd[16];
  913.     HFILE kfile;
  914.     struct MD5Context md5c;
  915.  
  916.     MD5Init(&md5c);
  917.     MD5Update(&md5c, d->buffer.buffer_val, (unsigned) d->buffer.buffer_len);
  918.     MD5Final(kmd, &md5c);
  919.  
  920.     if (_fmemcmp(conn->pgpkeymd5, kmd, 16) != 0) {
  921.         conn->pgpkey[0] = FALSE;
  922.         _fmemcpy(conn->pgpkeymd5, kmd, 16);
  923.         GetTempFileName(0, "PK", 0, conn->pgpFileName);
  924.  
  925.         kfile = _lcreat(conn->pgpFileName, 0);
  926.         if (kfile == NULL) {
  927.             MessageBox(hwnd, rstring(IDS_T_OPEN_SESSION_KEY_FILE_ERR),
  928.                 rstring(IDS_T_PGP_KEY_ERR_TITLE), MB_ICONEXCLAMATION | MB_OK);
  929.         } else {
  930.             UINT execStat = 0;
  931.         
  932.             _lwrite(kfile, d->buffer.buffer_val, (UINT) d->buffer.buffer_len);
  933.             _lclose(kfile);
  934.             
  935.             /*    First try to run PGP via the PIF in our own directory.  This
  936.                 guarantees it's run with the modes we've chosen, such as
  937.                 in a window rather than full-screen.  */
  938.             
  939.             if (GetModuleFileName(hInst, cmd, sizeof cmd) > 0) {
  940.                 char *cp = cmd + strlen(cmd);
  941.                 
  942.                 while (cp >= cmd && *cp != '\\' && *cp != ':') {
  943.                     cp--;
  944.                 }
  945.                 cp[1] = 0;
  946.                 wsprintf(cmd + strlen(cmd), Format(52), conn->pgpFileName);
  947.                 execStat = WinExec(cmd, SW_SHOW); 
  948.             }
  949.  
  950.             /*    If that didn't work, attempt to run PGP by straight path
  951.                 search using the default modes.  */
  952.                 
  953.             if (execStat < 32) {                       
  954.                 wsprintf(cmd, Format(1), conn->pgpFileName);
  955.                 execStat = WinExec(cmd, SW_SHOW);
  956.             }
  957.             
  958.             //    Set timer to poll for completion of decoding
  959.             
  960.             if (execStat >= 32) {
  961.                 conn->pgpFileName[_fstrlen(conn->pgpFileName) - 4] = 0;
  962.                 SetTimer(hwnd, 3, 1000, NULL);
  963.             } else {
  964.                 wsprintf(cmd + strlen(cmd), Format(51), execStat);
  965.                 MessageBox(hwnd, cmd, rstring(IDS_T_CANT_INVOKE_PGP),
  966.                        MB_OK | MB_ICONEXCLAMATION);
  967.             }
  968.         }
  969.     }
  970. }                           
  971.  
  972. //    onCreate  --  Initialise frame window when newly created
  973.  
  974. static BOOL onCreate(HWND hwnd, CREATESTRUCT FAR *pCreateStruct)
  975. {
  976.     CLIENTCREATESTRUCT ccs;
  977.     WSADATA wsadata;
  978.     SOCKERR serr;
  979.     HDC hdc;
  980.     TEXTMETRIC tm;
  981.     int i, j;
  982.     char *cg;
  983.     char pfn[80];
  984.     char md5key[16];
  985.  
  986.     //  Initialize the sockets library
  987.  
  988.     serr = WSAStartup(DESIRED_WINSOCK_VERSION, &wsadata);
  989.  
  990.     if (serr != 0) {
  991.         MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(12), serr, SockerrToString(serr));
  992.         return FALSE;
  993.     }
  994.  
  995.     if (wsadata.wVersion < MINIMUM_WINSOCK_VERSION) {
  996.         MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(13), LOBYTE(wsadata.wVersion),
  997.                 HIBYTE(wsadata.wVersion), LOBYTE(MINIMUM_WINSOCK_VERSION),
  998.                 HIBYTE(MINIMUM_WINSOCK_VERSION));
  999.         return FALSE;
  1000.     }
  1001.     aboutUDPmax = wsadata.iMaxUdpDg;
  1002.     
  1003.     /*    If the network can't transmit sound packets as large as we'd like
  1004.         to send, restrict the size to what it can accommodate.  */
  1005.         
  1006.     if (aboutUDPmax > 0) {
  1007.         if ((netMaxSamples + (sizeof(soundbuf) - BUFL) + 40) > (WORD) aboutUDPmax) {
  1008.             netMaxSamples = aboutUDPmax - ((sizeof(soundbuf) - BUFL) + 40); 
  1009.         }
  1010.     }
  1011.  
  1012.     //  Create the command socket
  1013.  
  1014.     serr = CreateSocket(&sCommand, SOCK_DGRAM, htonl(INADDR_ANY), htons(NETFONE_COMMAND_PORT));
  1015.     if (serr != 0) {
  1016.         MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(14),
  1017.                 serr, SockerrToString(serr));
  1018.         return FALSE;
  1019.     }
  1020.    
  1021.     //    Load cursors
  1022.     
  1023.     phoneCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR_PHONE));
  1024.     earCursor = LoadCursor(hInst, MAKEINTRESOURCE(IDC_CURSOR_EAR));
  1025.     
  1026.     //    Register the message for file open/save help button pressed
  1027.     
  1028.     fileOpenHelpButton = RegisterWindowMessage(HELPMSGSTRING);
  1029.     
  1030.     //    Initialise GSM encoding and decoding
  1031.  
  1032.     gsmh = gsm_create();
  1033.  
  1034.     // Initialise LPC encoding and decoding
  1035.  
  1036.     lpc_init(180);
  1037.     
  1038.     //    Initialise DES
  1039.     
  1040.     desinit(1);
  1041.     
  1042.     //    Initialise RTP randomised parameters
  1043.  
  1044.     sessionKeyGenerate(md5key, TRUE);
  1045.     _fmemcpy((char *) &ssrc, md5key, sizeof ssrc);
  1046.     _fmemcpy((char *) ×tamp, md5key + sizeof ssrc,
  1047.           sizeof timestamp);
  1048.     _fmemcpy((char *) &seq, md5key + sizeof ssrc + sizeof timestamp,
  1049.           sizeof seq);
  1050.  
  1051.     //  Create the MDI client window
  1052.  
  1053.     ccs.hWindowMenu  = GetSubMenu(GetMenu(hwnd), 3);    // Identify Window menu
  1054.     ccs.idFirstChild = IDM_WINDOW_FIRST_CHILD;
  1055.  
  1056.     hwndMDIClient = CreateWindow(pszMDIClientClass, NULL,
  1057.                                  WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL,
  1058.                                  0, 0, 0, 0, hwnd, 0, hInst, (LPSTR) &ccs);
  1059.  
  1060.     if (hwndMDIClient == NULL) {
  1061.         return FALSE;
  1062.     }
  1063.  
  1064.     ShowWindow(hwndMDIClient, SW_SHOW);
  1065.     
  1066.     _fstrcpy(pfn, rstring(IDS_PF_PROFILE_FILE));
  1067.     
  1068.     //    Load remembered recent connections
  1069.     
  1070.     cg = rstring(IDS_PF_CONNECTIONS);
  1071.     rememberedConnections = GetPrivateProfileInt(cg, rstring(IDS_PF_CONNCOUNT),
  1072.         0, pfn);
  1073.     if (rememberedConnections > REMEMBER_CONNECTIONS) {
  1074.         rememberedConnections = REMEMBER_CONNECTIONS;
  1075.     }
  1076.     for (i = j = 0; i < rememberedConnections; i++) {
  1077.         char psn[12], rcr[MAX_HOST];
  1078.         
  1079.         wsprintf(psn, Format(2), i + 1);
  1080.         GetPrivateProfileString(cg, psn, "", rcr,
  1081.             sizeof rcr, pfn);
  1082.         if (_fstrlen(rcr) > 0) {
  1083.             LPSTR rcon = (LPSTR) GlobalAllocPtr(GPTR, _fstrlen(rcr) + 1);
  1084.             
  1085.             if (rcon!= NULL) {
  1086.                 _fstrcpy(rcon, rcr);
  1087.                 rememberedConnection[j++] = rcon;
  1088.             } 
  1089.         } 
  1090.     }
  1091.     rememberedConnections = j;
  1092.     
  1093.     //    Restore multicast group memberships
  1094.     
  1095.     cg = rstring(IDS_PF_MULTIGROUPS);
  1096.     multiMemberships = GetPrivateProfileInt(cg, rstring(IDS_PF_CONNCOUNT),
  1097.         0, pfn);
  1098.     if (multiMemberships > IP_MAX_MEMBERSHIPS) {
  1099.         multiMemberships = IP_MAX_MEMBERSHIPS;
  1100.     }
  1101.     for (i = 0; i < multiMemberships; i++) {
  1102.         char psn[24], rcr[MAX_HOST];
  1103.         
  1104.         wsprintf(psn, Format(3), i + 1);
  1105.         GetPrivateProfileString(cg, psn, "", rcr,
  1106.             sizeof rcr, pfn);
  1107.         multiAddr[i].s_addr = inet_addr(rcr);
  1108.         wsprintf(psn, Format(4), i + 1);
  1109.         GetPrivateProfileString(cg, psn, "", rcr, sizeof rcr, pfn);
  1110.         multiName[i] = NULL;
  1111.         if (_fstrlen(rcr) > 0) {
  1112.             LPSTR rcon = (LPSTR) GlobalAllocPtr(GPTR, _fstrlen(rcr) + 1);
  1113.             
  1114.             if (rcon!= NULL) {
  1115.                 _fstrcpy(rcon, rcr);
  1116.                 multiName[i] = rcon;
  1117.             } 
  1118.         } 
  1119.     }
  1120.     multiLoop = GetPrivateProfileInt(cg, rstring(IDS_PF_LOOPBACK), TRUE, pfn);
  1121.     multicastJoin(hwnd, TRUE);
  1122.     
  1123.     //    Restore compression modes
  1124.     
  1125. #define BoolProfile(var, section, itemname, defval) var = GetPrivateProfileInt(section, itemname, defval, pfn)     
  1126.     cg = rstring(IDS_PF_COMPRESSION);
  1127.     BoolProfile(compression, cg, rstring(IDS_PF_COMP_SIMPLE), FALSE);
  1128.     BoolProfile(gsmcompress, cg, rstring(IDS_PF_COMP_GSM), TRUE);
  1129.     BoolProfile(adpcmcompress, cg, rstring(IDS_PF_COMP_ADPCM), FALSE);
  1130.     BoolProfile(lpccompress, cg, rstring(IDS_PF_COMP_LPC), FALSE);
  1131.      
  1132.     //    Restore modem configuration and initialise if enabled
  1133.     
  1134.     cg = rstring(IDS_PF_MODEM);
  1135.     BoolProfile(modemEnable, cg, rstring(IDS_PF_ENABLE), FALSE);
  1136.     GetPrivateProfileString(cg, rstring(IDS_PF_PORT), "COM1", commport,
  1137.             sizeof commport, pfn);
  1138.     GetPrivateProfileString(cg, rstring(IDS_PF_BAUDRATE), "19200", baudrate,
  1139.             sizeof baudrate, pfn);
  1140.     GetPrivateProfileString(cg, rstring(IDS_PF_MODEM_INIT_STRING), "ATQ0V1E1S0=1",
  1141.             modemInitString, sizeof modemInitString, pfn);
  1142.     BoolProfile(modemShowRant, cg, rstring(IDS_PF_SHOW_RANT), TRUE);
  1143.     
  1144.     //    Restore answering machine settings
  1145.     
  1146.     cg = rstring(IDS_PF_ANSWER);
  1147.     GetPrivateProfileString(cg, rstring(IDS_PF_FILE_NAME), "", answerFileName,
  1148.             sizeof answerFileName, pfn);
  1149.        BoolProfile(answerRecord, cg, rstring(IDS_PF_RECORD_MESSAGES), FALSE);
  1150.        
  1151.     //    Restore in your face settings
  1152.     
  1153.     GetPrivateProfileString(rstring(IDS_PF_FACE), rstring(IDS_PF_FILE_NAME), "", faceFileName,
  1154.             sizeof faceFileName, pfn);
  1155.        BoolProfile(faceShow, rstring(IDS_PF_FACE), rstring(IDS_PF_SHOW_FACES), TRUE);    
  1156.             
  1157.     //    Restore miscellaneous configuration parameters
  1158.     
  1159.     GetPrivateProfileString(rstring(IDS_PF_RING), rstring(IDS_PF_FILE_NAME), "",
  1160.             ringFileName, sizeof ringFileName, pfn);
  1161.     BoolProfile(lookWho_sTalking, rstring(IDS_PF_PREFERENCES), rstring(IDS_PF_LOOKWHO), FALSE);
  1162.  
  1163.     //    Restore Look Who's Listening parameters
  1164.     
  1165.     GetPrivateProfileString(rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_SERVER), "",
  1166.             lwl_s_server, sizeof lwl_s_server, pfn);
  1167.     GetPrivateProfileString(rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_EMAIL), "",
  1168.             lwl_s_email, sizeof lwl_s_email, pfn);
  1169.     GetPrivateProfileString(rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_FULLNAME), "",
  1170.             lwl_s_fullname, sizeof lwl_s_fullname, pfn);
  1171.     GetPrivateProfileString(rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_PHONE), "",
  1172.             lwl_s_phone, sizeof lwl_s_phone, pfn);
  1173.     GetPrivateProfileString(rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_LOCATION), "",
  1174.             lwl_s_location, sizeof lwl_s_location, pfn);
  1175.     BoolProfile(lwl_s_publish, rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_PUBLISH), FALSE);
  1176.     BoolProfile(lwl_s_exact, rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_EXACT), FALSE);
  1177.  
  1178.     GetPrivateProfileString(rstring(IDS_PF_LWL_ASK), rstring(IDS_PF_SERVER), "",
  1179.             lwl_a_server, sizeof lwl_a_server, pfn);
  1180.     BoolProfile(lwl_a_exact, rstring(IDS_PF_LWL_ASK), rstring(IDS_PF_EXACT), FALSE);
  1181.             
  1182.     //    Restore bug workaround parameters
  1183.     
  1184.     cg = rstring(IDS_PF_WORKAROUNDS);
  1185.     BoolProfile(alwaysBindSocket, cg, rstring(IDS_PF_ALWAYS_BIND), FALSE);    
  1186.     BoolProfile(waNetNoConnect, cg, rstring(IDS_PF_NET_NO_CONNECT), FALSE);    
  1187.     BoolProfile(waNetUseSend, cg, rstring(IDS_PF_NET_USE_SEND), FALSE);    
  1188.     BoolProfile(waNetMultiTTLisChar, cg, rstring(IDS_PF_MULTICAST_CHAR_ARG), FALSE);    
  1189.     BoolProfile(waAudioHalf, cg, rstring(IDS_PF_AUDIO_HALF_DUPLEX), FALSE);    
  1190.     BoolProfile(waAudio11025, cg, rstring(IDS_PF_AUDIO_SAMPLE_11025), FALSE);    
  1191. #undef BoolProfile
  1192.     useSendNotSendto = waNetUseSend; 
  1193.     
  1194.     //    Check out the audio hardware
  1195.     
  1196.     halfDuplex = isHalfDuplex(hwnd);
  1197.  
  1198.     //  Create the timeout timer
  1199.  
  1200.     if (SetTimer(hwnd, FRAME_TIMER_ID, 1000, NULL) == 0) {
  1201.         MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(15));
  1202.         return FALSE;
  1203.     }
  1204.             
  1205.     //    Calculate packet size based on selected modes
  1206.     
  1207.     currentInputLength = inputBufferLength();
  1208.     currentInputSamples = inputSampleCount();    
  1209.  
  1210.     //  Get textmetric data
  1211.  
  1212.     hdc = GetDC(hwnd);
  1213.     GetTextMetrics(hdc, &tm);
  1214.     ReleaseDC(hwnd, hdc);
  1215.  
  1216.     tmAveCharWidth = (INT)tm.tmAveCharWidth;
  1217.     tmHeight = (INT)tm.tmHeight;
  1218.     pfnPropeller = MakeProcInstance((FARPROC) propellerHeadDlgProc, hInst);
  1219.     pfnAnswer = MakeProcInstance((FARPROC) answerDlgProc, hInst);
  1220.     
  1221.     /*    If we were invoked with file names on the command line,
  1222.         as happens when a .SFX file is double clicked in the File
  1223.         Manager (assuming the association has been made), open the
  1224.         specified connection files.  */
  1225.     
  1226.     if (commandLine != NULL) {
  1227.         LPSTR cln, clp = commandLine;
  1228.         
  1229.         while (_fstrlen(clp) > 0) {
  1230.             while (_fstrlen(clp) > 0 && isspace(*clp)) {
  1231.                 clp++;
  1232.             }
  1233.             if (*clp == 0) {
  1234.                 break;
  1235.             }
  1236.             cln = _fstrchr(clp, ' ');
  1237.             if (cln != NULL) {
  1238.                 *cln = 0;
  1239.             }
  1240.             newConnection(hwnd, clp, NULL);
  1241.             if (cln == NULL) {
  1242.                 break;
  1243.             }
  1244.             clp = cln + 1;
  1245.         }
  1246.         GlobalFreePtr(commandLine);
  1247.         commandLine = NULL;
  1248.     }
  1249.     
  1250.     DragAcceptFiles(hwnd, TRUE);
  1251.     
  1252.     /*    If an answering machine message file was named, open
  1253.         it.  This doesn't imply we're recording messages; that's
  1254.         controlled by answerRecord.  */
  1255.     
  1256.     if (answerFileName[0] != 0) {
  1257.         answerOpen();
  1258.     }
  1259.     
  1260.     //    Open the face image file, if any
  1261.     
  1262.     V openFaceFile(hwnd);
  1263.  
  1264.     /*    If we're publishing our information with a Look Who's Listening
  1265.         server, establish contact with it.  */
  1266.         
  1267.     V lwl_reconnect(hwnd);
  1268.     
  1269.     //    Finally, open the input socket for business.
  1270.     
  1271.  
  1272.     if (WSAAsyncSelect(sCommand, hwnd, WM_SOCKET_SELECT, FD_READ) != 0) {
  1273.         serr = WSAGetLastError();
  1274.     }
  1275.     if (serr != 0) {
  1276.         MsgBox(NULL, MB_ICONSTOP | MB_OK, Format(14),
  1277.                 serr, SockerrToString(serr));
  1278.     }    
  1279.     return TRUE;
  1280. }
  1281.  
  1282. /*  onCommand  --  Dispatch WM_COMMAND messages.  Some are processed
  1283.                    at the frame level, some sent to the MDI client,
  1284.                    and others forwarded to the active MDI connection
  1285.                    window. */
  1286.  
  1287. static VOID onCommand(HWND hwnd, INT id, HWND hwndCtl, UINT codeNotify)
  1288. {
  1289.     if (id > IDM_CUSTOM && id <= (IDM_CUSTOM + rememberedConnections)) {
  1290.         newConnection(hwnd, rememberedConnection[(id - IDM_CUSTOM) - 1], NULL);
  1291.         return;
  1292.     }
  1293.  
  1294.     switch (id) {
  1295.     
  1296.         case IDM_CONNECTION_NEW:
  1297.             newConnection(hwnd, NULL, NULL);
  1298.             break;
  1299.             
  1300.         case IDM_CONN_OPEN:
  1301.              {
  1302.                  OPENFILENAME ofn;
  1303.                  char cfName[MAX_PATH];
  1304.                  
  1305.                 memset(&ofn, 0, sizeof(ofn));
  1306.                 ofn.lStructSize = sizeof(OPENFILENAME);
  1307.                 ofn.hwndOwner = hwnd;
  1308.                 ofn.lpstrFilter = rfilter(IDS_T_CONNECTION_FILTER);
  1309.                 ofn.lpstrCustomFilter = NULL;
  1310.                 cfName[0] = 0;
  1311.                 ofn.lpstrFile = (LPSTR) cfName;
  1312.                 ofn.nMaxFile = sizeof cfName;
  1313.                 ofn.lpstrInitialDir = NULL;
  1314.                 ofn.lpstrTitle = rstring(IDS_T_CONNECTION_OPEN_TITLE);
  1315.                 ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_SHOWHELP;
  1316.                 fileHelpKey = rstring(IDS_HELP_OPEN_CONNECTION);
  1317.                 if (GetOpenFileName((LPOPENFILENAME) &ofn) == 0) {
  1318.                     break;
  1319.                 }
  1320.                 newConnection(hwnd, cfName, NULL);
  1321.              }
  1322.             break;
  1323.             
  1324.         case IDM_CONN_SAVE_AS:
  1325.              {
  1326.                  OPENFILENAME ofn;
  1327.                  HWND cWnd = getActiveConnection();
  1328.                  
  1329.                  if (cWnd != NULL) {
  1330.                      LPCLIENT_DATA d = CLIENTPTR(cWnd);
  1331.                  
  1332.                     d->timeout = -1;                // Make connection immortal
  1333.                     memset(&ofn, 0, sizeof(ofn));
  1334.                     ofn.lStructSize = sizeof(OPENFILENAME);
  1335.                     ofn.hwndOwner = hwnd;
  1336.                     ofn.lpstrDefExt = "SFX";
  1337.                     ofn.lpstrFilter = rfilter(IDS_T_CONNECTION_FILTER);
  1338.                     ofn.lpstrCustomFilter = NULL;
  1339.                     d->szFile[0] = 0;
  1340.                     ofn.lpstrFile = (LPSTR) d->connectionFileName;
  1341.                     ofn.nMaxFile = sizeof d->connectionFileName;
  1342.                     ofn.lpstrInitialDir = NULL;
  1343.                     ofn.lpstrTitle = rstring(IDS_T_CONNECTION_SAVE_TITLE);
  1344.                     ofn.Flags = OFN_NOREADONLYRETURN | OFN_OVERWRITEPROMPT |
  1345.                                 OFN_HIDEREADONLY | OFN_SHOWHELP;
  1346.                     fileHelpKey = rstring(IDS_HELP_SAVE_CONNECTION);
  1347.                     if (GetSaveFileName((LPOPENFILENAME) &ofn) == 0) {
  1348.                         break;
  1349.                     }
  1350.                 }
  1351.              }
  1352.              //    Wheee!!!  Note fall-through
  1353.              
  1354.              case IDM_CONN_SAVE:
  1355.              {
  1356.                  HWND cWnd = getActiveConnection();
  1357.                  
  1358.                  if (cWnd != NULL) {
  1359.                      LPCLIENT_DATA d = CLIENTPTR(cWnd);
  1360.                      char mcs[10];
  1361.                      
  1362.                      wsprintf(mcs, Format(5), d->multicast_scope);
  1363.                     d->timeout = -1;                // Make connection immortal
  1364.                      if (WritePrivateProfileString(rstring(IDS_PF_HOST), rstring(IDS_PF_HOST_NAME),
  1365.                              d->szHost, d->connectionFileName) == 0 ||
  1366.                          WritePrivateProfileString(rstring(IDS_PF_HOST), rstring(IDS_PF_NETADDR),
  1367.                              inet_ntoa(d->inetSock.sin_addr), d->connectionFileName) == 0 ||
  1368.                          WritePrivateProfileString(rstring(IDS_PF_DEBUG), rstring(IDS_PF_DEBUGGING),
  1369.                              d->debugging ? kS1 : kS0, d->connectionFileName) == 0 ||
  1370.                          WritePrivateProfileString(rstring(IDS_PF_DEBUG), rstring(IDS_PF_LOOPBACK),
  1371.                              d->loopback ? kS1 : kS0, d->connectionFileName) == 0 ||
  1372.                          WritePrivateProfileString(rstring(IDS_PF_MULTICAST), rstring(IDS_PF_SCOPE),
  1373.                              mcs, d->connectionFileName) == 0 ||
  1374.                          WritePrivateProfileString(rstring(IDS_PF_ENCRYPTION), rstring(IDS_PF_SAVE_KEYS),
  1375.                              d->saveKeys ? kS1 : kS0, d->connectionFileName) == 0 ||
  1376.                          WritePrivateProfileString(rstring(IDS_PF_ENCRYPTION), rstring(IDS_PF_PGP_USER_NAMES),
  1377.                              d->opgpUserList, d->connectionFileName) == 0 |
  1378.                          (!d->saveKeys ? 0 : (
  1379.                              WritePrivateProfileString(rstring(IDS_PF_ENCRYPTION), rstring(IDS_PF_DES_KEY),
  1380.                                  d->desKeyString, d->connectionFileName) == 0 ||
  1381.                              WritePrivateProfileString(rstring(IDS_PF_ENCRYPTION), rstring(IDS_PF_IDEA_KEY),
  1382.                                  d->ideaKeyString, d->connectionFileName) == 0 ||
  1383.                              WritePrivateProfileString(rstring(IDS_PF_ENCRYPTION), rstring(IDS_PF_KEY_FILE),
  1384.                                  d->otpFileName, d->connectionFileName) == 0))) {
  1385.                             MessageBox(hwnd, rstring(IDS_T_CONN_SAVE_ERR), NULL,
  1386.                                    MB_OK | MB_ICONEXCLAMATION);
  1387.                      } else {
  1388.                         rememberNewConnection(d->connectionFileName);
  1389.                      }
  1390.                  }
  1391.             }
  1392.             break;            
  1393.             
  1394.         case IDM_CONN_PROPERTIES:
  1395.             {
  1396.                 HWND cWnd = getActiveConnection();
  1397.                 
  1398.                 if (cWnd != NULL) {
  1399.                     CLIENTPTR(cWnd)->timeout = -1;        // Make connection immortal
  1400.                     connectionProperties(cWnd, CLIENTPTR(cWnd));
  1401.                 }
  1402.             }
  1403.             break;
  1404.  
  1405.         case IDM_CONN_RING:
  1406.              {
  1407.                  HWND cWnd = getActiveConnection();
  1408.                  
  1409.                  if (cWnd != NULL) {
  1410.                      CLIENTPTR(cWnd)->ring = TRUE;
  1411.                  }
  1412.             }
  1413.             //    Wheee!!!  Fall through into Send Sound File
  1414.             
  1415.         case IDM_SEND_SOUND_FILE:
  1416.             {
  1417.                  OPENFILENAME ofn;
  1418.                  HWND cWnd = getActiveConnection();
  1419.                  
  1420.                  if (cWnd != NULL) {
  1421.                      LPCLIENT_DATA d = CLIENTPTR(cWnd);
  1422.                      int originalTimeout = d->timeout;
  1423.                      
  1424.                      /* Make the window "temporarily immortal" so it doesn't
  1425.                         disappear due to timeout while the send file dialogue
  1426.                         is up. */
  1427.                     d->timeout = -1;
  1428.                     if (d->hFile == HFILE_ERROR && d->mmioHandle == NULL) {
  1429.                         int s = FALSE;
  1430.                         
  1431.                         memset(&ofn, 0, sizeof(ofn));
  1432.                         ofn.lStructSize = sizeof(OPENFILENAME);
  1433.                         ofn.hwndOwner = hwnd;
  1434.                         ofn.lpstrFilter = rfilter(IDS_T_SOUND_FILE_FILTER);
  1435.                         ofn.lpstrCustomFilter = NULL;
  1436.                         d->szFile[0] = 0;
  1437.                         ofn.lpstrFile = (LPSTR) d->szFile;
  1438.                         ofn.nMaxFile = sizeof(d->szFile);
  1439.                         ofn.lpstrInitialDir = NULL;
  1440.                         ofn.lpstrTitle = d->ring ? rstring(IDS_T_RING_TITLE) :
  1441.                                                    rstring(IDS_T_SEND_SOUND_TITLE);
  1442.                         ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_SHOWHELP;
  1443.                         fileHelpKey = d->ring ? rstring(IDS_HELP_RING_FILE) :
  1444.                                                 rstring(IDS_HELP_SEND_SOUND_FILE);
  1445.                         if (d->ring && ringFileName[0] != 0) {
  1446.                             HFILE hFile;
  1447.                             
  1448.                             hFile = _lopen(ringFileName, OF_READ | OF_SHARE_DENY_WRITE);
  1449.                             if (hFile != HFILE_ERROR) {
  1450.                                 _lclose(hFile);
  1451.                                 _fstrcpy(d->szFile, ringFileName);
  1452.                                 s = TRUE;
  1453.                             }
  1454.                         }
  1455.                         if (!s) {
  1456.                             s = GetOpenFileName((LPOPENFILENAME) &ofn);
  1457.                             if (d->ring) {
  1458.                                 _fstrcpy(ringFileName, d->szFile);
  1459.                             }
  1460.                         } 
  1461.                         if (s) {
  1462.                             startSoundFile(cWnd, d->szFile);
  1463.                         } else {
  1464.                             /*    User canceled send file dialogue.  Restore
  1465.                                 timeout to the original value.  */
  1466.                             d->timeout = originalTimeout;
  1467.                         }
  1468.                     } else {
  1469.                         d->quitSoundFile = TRUE;
  1470.                     }
  1471.                 }
  1472.              }
  1473.             break;
  1474.              
  1475.         case IDM_CO_REPONDEUR:
  1476.             answerDialogue(hwnd);
  1477.             break;
  1478.             
  1479.         case IDM_CO_RECORD:
  1480.             answerRecord = !answerRecord;
  1481.             if (hDlgAnswer != NULL) {
  1482.                 CheckDlgButton(hDlgAnswer, IDC_RP_RECORD, answerEnabled() && answerRecord);
  1483.             }
  1484.             break;
  1485.             
  1486.         case IDM_CONN_BROADCAST:
  1487.             broadcasting = !broadcasting;
  1488.             if (broadcasting) {
  1489.                 HWND hwnd = GetWindow(hwndMDIClient, GW_CHILD);
  1490.                 DWORD bstart = GetTickCount();
  1491.  
  1492.                 //    Clear wants input in any sending windows
  1493.             
  1494.                 while (hwnd != NULL) {
  1495.                     if ((WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC) == ((WNDPROC) connectWndProc)) {
  1496.                         LPCLIENT_DATA pClientData = CLIENTPTR(hwnd);
  1497.                 
  1498.                         if (pClientData != NULL) {
  1499.                             pClientData->wantsInput = FALSE;
  1500.                             pClientData->broadcastBeginTime = bstart;
  1501.                             pClientData->broadcastEnd = FALSE;
  1502.                         }
  1503.                     }
  1504.                     hwnd = GetWindow(hwnd, GW_HWNDNEXT);
  1505.                 }
  1506.                 listeners = 0;
  1507.                 if (!inputActive) {
  1508.                     V startWaveInput(hwnd);
  1509.                 }
  1510.             } else {
  1511.                 terminateWaveInput();
  1512.                 SetCursor(LoadCursor(NULL, IDC_ARROW));
  1513.             }
  1514.             SetWindowText(hwndMDIFrame, rstring(broadcasting ?
  1515.                 IDS_T_APP_TITLE_BROADCAST : IDS_T_APP_TITLE_NORM));
  1516.             break;
  1517.             
  1518.         case IDM_RING_FILE_NAME:
  1519.             {
  1520.                  OPENFILENAME ofn;
  1521.                  char fName[MAX_PATH];
  1522.                  
  1523.                 memset(&ofn, 0, sizeof(ofn));
  1524.                 ofn.lStructSize = sizeof(OPENFILENAME);
  1525.                 ofn.hwndOwner = hwnd;
  1526.                 ofn.lpstrFilter = rfilter(IDS_T_SOUND_FILE_FILTER);
  1527.                 ofn.lpstrCustomFilter = NULL;
  1528.                 _fstrcpy(fName, ringFileName);
  1529.                 ofn.lpstrFile = (LPSTR) fName;
  1530.                 ofn.nMaxFile = sizeof(fName);
  1531.                 ofn.lpstrInitialDir = NULL;
  1532.                 ofn.lpstrTitle = rstring(IDS_T_RING_TITLE);
  1533.                 ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_SHOWHELP;
  1534.                 fileHelpKey = rstring(IDS_HELP_RING_FILE);
  1535.                 if (GetOpenFileName((LPOPENFILENAME) &ofn)) {
  1536.                     _fstrcpy(ringFileName, fName);
  1537.                 }
  1538.              }
  1539.             break;
  1540.             
  1541.         case IDM_CONN_MULTICAST:
  1542.             multicastGroupsDialogue(hwnd);
  1543.             break;
  1544.  
  1545.         case IDM_DISCONNECT:
  1546.             {
  1547.                 LRESULT mdiActive = SendMessage(hwndMDIClient, WM_MDIGETACTIVE,
  1548.                                             0, 0L);
  1549.                 if (LOWORD(mdiActive) != NULL) {
  1550.                     PostMessage((HWND) LOWORD(mdiActive), WM_CLOSE, 0, 0L);
  1551.                 }
  1552.             }
  1553.             break;
  1554.             
  1555.         case IDM_OPT_8BIT:
  1556.             audioUse8Bit = !audioUse8Bit;
  1557.             if (!audioUse8Bit && !audioIs8Bit) {
  1558.                 sampleAlignment = 2;
  1559.                 bitsPerSample = 16;
  1560.             } else {
  1561.                 sampleAlignment = 1;
  1562.                 bitsPerSample = 8;
  1563.             }
  1564.             bytesPerSecond = samplesPerSecond * sampleAlignment;
  1565.             propUpdateAudio();
  1566.             break;
  1567.         
  1568.         case IDM_OPT_LOOK_WHO:
  1569.             lookWho_sTalking = !lookWho_sTalking;
  1570.             break;    
  1571.                     
  1572.         case IDM_WORKA_BIND:
  1573.             alwaysBindSocket = !alwaysBindSocket;
  1574.             break;
  1575.             
  1576.         case IDM_WORKA_NOCONNECT:
  1577.             waNetNoConnect = !waNetNoConnect;
  1578.             if (waNetNoConnect) {
  1579.                 waNetUseSend = FALSE;
  1580.             }
  1581.             break;
  1582.             
  1583.         case IDM_WORKA_USE_SEND:
  1584.             waNetUseSend = !waNetUseSend;
  1585.             useSendNotSendto = waNetUseSend; 
  1586.             if (waNetUseSend) {
  1587.                 waNetNoConnect = FALSE;
  1588.             }
  1589.             if (hDlgPropeller != NULL) {
  1590.                 SetDlgItemText(hDlgPropeller, IDC_PH_SENDTO,
  1591.                     useSendNotSendto ? rstring(IDS_T_SEND) : rstring(IDS_T_SENDTO));
  1592.             }
  1593.             break;
  1594.             
  1595.         case IDM_WORKA_TTLCHAR:
  1596.             waNetMultiTTLisChar = !waNetMultiTTLisChar;
  1597.             break;
  1598.             
  1599.         case IDM_WORKA_HALF_DUPLEX:
  1600.             waAudioHalf = !waAudioHalf;
  1601.             MessageBox(hwnd, rstring(IDS_T_EXIT_AND_RESTART), pszAppName,
  1602.                    MB_OK | MB_ICONINFORMATION);            
  1603.             break;
  1604.             
  1605.         case IDM_WORKA_SAMPLE_11025:
  1606.             waAudio11025 = !waAudio11025;
  1607.             MessageBox(hwnd, rstring(IDS_T_EXIT_AND_RESTART), pszAppName,
  1608.                    MB_OK | MB_ICONINFORMATION);            
  1609.             break;
  1610.             
  1611.         case IDM_COMP_2X:
  1612.             compression = !compression;
  1613.             currentInputLength = inputBufferLength();
  1614.             currentInputSamples = inputSampleCount();
  1615.             propUpdateAudio();
  1616.             break;
  1617.             
  1618.         case IDM_COMP_ADPCM:
  1619.             adpcmcompress = !adpcmcompress;
  1620.             if (adpcmcompress) {
  1621.                 gsmcompress = FALSE;
  1622.                 lpccompress = FALSE;
  1623.             }
  1624.             currentInputLength = inputBufferLength();
  1625.             currentInputSamples = inputSampleCount();
  1626.             propUpdateAudio();
  1627.             break;
  1628.             
  1629.         case IDM_COMP_LPC:
  1630.             lpccompress = !lpccompress;
  1631.             if (lpccompress) {
  1632.                 gsmcompress = FALSE;
  1633.                 adpcmcompress = FALSE;
  1634.             }
  1635.             currentInputLength = inputBufferLength();
  1636.             currentInputSamples = inputSampleCount();
  1637.             propUpdateAudio();
  1638.             break;
  1639.             
  1640.         case IDM_COMP_GSM:
  1641.             gsmcompress = !gsmcompress;
  1642.             if (gsmcompress) {
  1643.                 adpcmcompress = FALSE;
  1644.                 lpccompress = FALSE;
  1645.             }
  1646.             currentInputLength = inputBufferLength();
  1647.             currentInputSamples = inputSampleCount();
  1648.             propUpdateAudio();
  1649.             break;
  1650.             
  1651.         case IDM_OPT_MODEM:
  1652.             modemSetupDialogue(hwnd);
  1653.             break;
  1654.             
  1655.         case IDM_OPT_FACE:
  1656.             faceDialogue(hwnd);
  1657.             break;
  1658.             
  1659.         case IDM_CRYPTO_GENKEY:
  1660.             genKeyDialogue(hwnd);
  1661.             break;
  1662.     
  1663.         case IDM_CONNECTION_EXIT:
  1664.             PostMessage(hwnd, WM_CLOSE, 0, 0);
  1665.             break;
  1666.             
  1667.         case IDM_DIR_SEARCH:
  1668.             lwl_t_diactive = TRUE;
  1669.             lwl_ask(hwnd);
  1670.             lwl_t_diactive = FALSE;
  1671.             break;
  1672.             
  1673.         case IDM_DIR_LISTING:
  1674.             lwl_t_diactive = TRUE;
  1675.             lwl_tell_settings(hwnd);
  1676.             lwl_t_diactive = FALSE;
  1677.             break;
  1678.     
  1679.         case IDM_WINDOW_CASCADE:
  1680.             FORWARD_WM_MDICASCADE(hwndMDIClient, 0, SendMessage);
  1681.             break;
  1682.     
  1683.         case IDM_WINDOW_TILE_VERTICALLY:
  1684.             FORWARD_WM_MDITILE(hwndMDIClient, MDITILE_VERTICAL, SendMessage);
  1685.             break;
  1686.     
  1687.         case IDM_WINDOW_TILE_HORIZONTALLY :
  1688.             FORWARD_WM_MDITILE(hwndMDIClient, MDITILE_HORIZONTAL, SendMessage);
  1689.             break;
  1690.     
  1691.         case IDM_WINDOW_ARRANGE_ICONS:
  1692.             FORWARD_WM_MDIICONARRANGE(hwndMDIClient, SendMessage);
  1693.             break;
  1694.  
  1695.          case IDM_HELP_CONT:
  1696.              WinHelp(hwnd, rstring(IDS_HELPFILE), HELP_CONTENTS, 0L);
  1697.              holped = TRUE;
  1698.              break;
  1699.  
  1700.          case IDM_HELP_SEARCH:
  1701.              WinHelp(hwnd, rstring(IDS_HELPFILE), HELP_PARTIALKEY, ((DWORD) ((LPSTR) "")));
  1702.              holped = TRUE;
  1703.              break;
  1704.              
  1705.         case IDM_PROPELLER_HEAD:
  1706.             propellerHeadDialogue(hwnd);
  1707.             break;
  1708.     
  1709.         case IDM_HELP_ABOUT:
  1710.             aboutDialogue(hwnd);
  1711.             break;
  1712.             
  1713.         case IDM_MODEM_RANT:
  1714.             modemRant(hwnd);
  1715.             break;
  1716.     
  1717.         default:
  1718.             FORWARD_WM_COMMAND(hwnd, id, hwndCtl,
  1719.                                codeNotify, Frame_MDIMessageForwarder);
  1720.             break;
  1721.     }
  1722. }
  1723.  
  1724. //    OBTAINOUTPUT  --  Try to obtain control of wave audio output
  1725.  
  1726. int obtainOutput(HWND hwnd)
  1727. {
  1728.         
  1729.     //    Grab the audio device if we don't already have it
  1730.     
  1731.     outputTimeout = 0;                    // Reset release output timeout    
  1732.     if (!outputActive) {
  1733.         LPPCMWAVEFORMAT wf;    
  1734.         WORD woo;
  1735.         
  1736.         /* If we already know the port is half duplex, just ditch the
  1737.            packet right away rather than walking into the door again. */
  1738.             
  1739.         if (halfDuplex && inputActive) {
  1740.             propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
  1741.             return FALSE;
  1742.         } else {
  1743.         
  1744.             wf = (LPPCMWAVEFORMAT) GlobalAllocPtr(GPTR, sizeof(PCMWAVEFORMAT));
  1745.             if (wf == NULL) {
  1746.                 return FALSE;
  1747.             }
  1748.             wf->wf.wFormatTag = WAVE_FORMAT_PCM;
  1749.             wf->wf.nChannels = audioChannels;
  1750.             wf->wf.nSamplesPerSec = samplesPerSecond;
  1751.             wf->wf.nAvgBytesPerSec = bytesPerSecond;
  1752.             wf->wf.nBlockAlign = sampleAlignment;
  1753.             wf->wBitsPerSample = bitsPerSample;
  1754.             while (TRUE) {
  1755.                  woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  1756.                                          0L, 0L, WAVE_FORMAT_QUERY);
  1757.                  if (bitsPerSample > 8 && woo == WAVERR_BADFORMAT) {
  1758.                      audioIs8Bit = TRUE;
  1759.                  } 
  1760.                          
  1761.                  /* If our preferred mode (16 bit, 11025 samples/second) isn't
  1762.                     supported, try falling back to bottom-feeder 8 bit per sample
  1763.                     mode. */            
  1764.         
  1765.                  if ((audioUse8Bit || woo == WAVERR_BADFORMAT) && bitsPerSample > 8) {
  1766.                      bitsPerSample /= 2;
  1767.                      sampleAlignment /= 2;
  1768.                      bytesPerSecond /= 2;    
  1769.                     wf->wf.nAvgBytesPerSec = bytesPerSecond;
  1770.                     wf->wf.nBlockAlign = sampleAlignment;
  1771.                     wf->wBitsPerSample = bitsPerSample;
  1772.                      woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER, (LPWAVEFORMAT) wf,
  1773.                              0L, 0L, WAVE_FORMAT_QUERY);
  1774.                  }
  1775.                  
  1776.                  /* If we've failed to initialise in either 16 or 8 bit mode
  1777.                     at 8000 samples per second, it's possible the sound card
  1778.                     doesn't support any sampling mode below the Windows standard
  1779.                     of 11025 samples per second.  Have another go-round and see
  1780.                     if 11025 works. */
  1781.                  
  1782.                  if (woo == WAVERR_BADFORMAT && samplesPerSecond == 8000) {
  1783.                      samplesPerSecond = 11025;
  1784.                      bitsPerSample = 16;
  1785.                      sampleAlignment = bitsPerSample / 8;
  1786.                      bytesPerSecond = samplesPerSecond * sampleAlignment;  
  1787.                  } else {
  1788.                      break;
  1789.                  }
  1790.              } 
  1791.              
  1792.              if (woo != 0) {                
  1793.                 char et[MAXERRORLENGTH];
  1794.                 
  1795.                 waveOutGetErrorText(woo, et, sizeof et);
  1796.                 MessageBox(hwnd, et, rstring(IDS_T_WAVE_PLAY_FORMAT_ERR),
  1797.                        MB_OK | MB_ICONEXCLAMATION);
  1798.                 GlobalFreePtr(wf);
  1799.                 return FALSE;
  1800.             }
  1801.             if ((woo = waveOutOpen(&hWaveOut, (WORD) WAVE_MAPPER,
  1802.                   (LPWAVEFORMAT) wf, (DWORD) (UINT) hwnd, 0, (DWORD) CALLBACK_WINDOW)) != 0) {
  1803.                 char et[MAXERRORLENGTH];
  1804.                 
  1805.                 /* The next line looks wrong, doesn't it?  But I've seen drivers
  1806.                    for half-duplex sound boards that return NOTSUPPORTED instead
  1807.                    of ALLOCATED when you try to open input and output at the
  1808.                    same time. */
  1809.                 
  1810.                 if ((woo == MMSYSERR_ALLOCATED || woo == MMSYSERR_NOTSUPPORTED)
  1811.                     && inputActive) {
  1812.                     /* Yuck.  The fact that we're receiving audio input
  1813.                         appears to have rendered the audio output port
  1814.                         busy.  Silently sacrifice the packet on the altar
  1815.                         of this most un-creative sound bobbler. */ 
  1816.                     GlobalFreePtr(wf);
  1817.                     propeller(IDC_PH_INPUT_LOST, ++inputPacketsLost);
  1818.                     return FALSE;
  1819.                 }    
  1820.                 waveOutGetErrorText(woo, et, sizeof et);
  1821.                 MessageBox(hwnd, et, rstring(IDS_T_ERR_OPEN_WAVE_OUTPUT),
  1822.                     MB_OK | MB_ICONEXCLAMATION);
  1823.                 GlobalFreePtr(wf);
  1824.                 return FALSE;
  1825.             }
  1826.             GlobalFreePtr(wf);
  1827.             outputActive = TRUE;
  1828.             aboutOutSamples = samplesPerSecond;
  1829.             aboutOutBits = bitsPerSample;
  1830.             propUpdateAudio();
  1831.         }
  1832.     }
  1833.     return TRUE;
  1834. }
  1835.  
  1836. //    socketInput  --  Process audio packet received on the inbound socket
  1837.  
  1838. static VOID socketInput(HWND hwnd, SOCKET sock, SOCKERR serr, SOCKEVENT sevent)
  1839. {
  1840.     INT cbRead;
  1841.     SOCKADDR_IN addrClient;
  1842.     INT cbAddrClient;
  1843.     LPCLIENT_DATA pClientData = NULL;
  1844.     HWND hwndClient = NULL;
  1845.     soundbuf *d = &receivedSoundBuffer;
  1846.     static int errorBoxUp = FALSE;
  1847.     int hdxPacketLost = FALSE;
  1848.  
  1849. #ifdef ZZZ    
  1850.     if (lwl_t_diactive) {
  1851.         lwl_t_siclash = TRUE;
  1852.         lwl_t_sic_hwnd = hwnd;
  1853.         lwl_t_sic_sock = sock;
  1854.         lwl_t_sic_sockerr = serr;
  1855.         lwl_t_sic_sockevent = sevent;
  1856.         return;
  1857.     }
  1858.  
  1859. static int lwl_t_siclash;     
  1860. static HWND lwl_t_sic_hwnd;
  1861. static SOCKET lwl_t_sic_sock;
  1862. static SOCKERR lwl_t_sic_sockerr;
  1863. static SOCKEVENT lwl_t_sic_sockevent;
  1864. #endif 
  1865.     
  1866.     if (sevent == MODEM_INPUT_EVENT) {
  1867.         addrClient.sin_addr.s_addr = 0;
  1868.     } else {
  1869.         if ((sock != sCommand) || (sevent != FD_READ) || (serr != 0) || errorBoxUp) {
  1870.             //  Unknown command or unknown event or socket error.  Ignore it.
  1871.             return;
  1872.         }
  1873.     
  1874.         //  We got a packet.  Let's see what it tells us to do.
  1875.     
  1876.         cbAddrClient = sizeof(addrClient);
  1877.     
  1878.         cbRead = recvfrom(sock, (CHAR FAR *) d, sizeof(soundbuf), 0, 
  1879.                             (SOCKADDR FAR *) &addrClient,
  1880.                             &cbAddrClient);
  1881.     }
  1882.     propeller(IDC_PH_PACKETS_RECEIVED, ++packetsReceived);
  1883.     
  1884. if (lwl_t_diactive) {
  1885. return;
  1886. }
  1887.     
  1888.     //    Convert packet from network byte order to little-endian
  1889.     
  1890.     revlong(&(d->compression));
  1891.     revlong(&(d->buffer.buffer_len));
  1892.     
  1893.     //  See if a connection window already exists for this host
  1894.  
  1895.     hwndClient = findClientByHost(&addrClient);
  1896.  
  1897.     //  Create the connection window data if no window exists
  1898.     
  1899.     if (hwndClient != NULL) {
  1900.         pClientData = CLIENTPTR(hwndClient);
  1901.         if (pClientData->timeout > 0) {
  1902.             pClientData->timeout = 0;
  1903.         }
  1904.     } else {
  1905.         pClientData = (LPCLIENT_DATA) GlobalAllocPtr(GPTR, sizeof(CLIENT_DATA));
  1906.         if (pClientData != NULL) {
  1907.             _fmemset(pClientData, 0, sizeof(CLIENT_DATA));
  1908.             pClientData->dwType = WINDOW_TYPE_CLIENT;
  1909.             pClientData->wantsInput = FALSE;
  1910.             pClientData->broadcastBeginTime = GetTickCount();
  1911.             pClientData->broadcastEnd = FALSE;
  1912.             pClientData->state = Embryonic;
  1913.             pClientData->sReply = INVALID_SOCKET;
  1914.             pClientData->timeout = 0;
  1915.             pClientData->inetSock = addrClient;
  1916.             pClientData->modemConnection = addrClient.sin_addr.s_addr == 0; 
  1917.             pClientData->hFile = HFILE_ERROR;
  1918.             pClientData->szFile[0] = '\0';
  1919.             pClientData->face_stat = FSinit;
  1920. //pClientData->face_stat = -1;            
  1921.             pClientData->face_bmp = NULL;
  1922.             if (!pClientData->modemConnection) {
  1923.                 /* Set host only on socket connections.  Leave dial string
  1924.                    blank for remote-initiated modem connections. */
  1925.                 pClientData->szHost[0] = '(';
  1926.                 _fstrcpy(pClientData->szHost + 1, d->sendinghost);
  1927.                 _fstrcat(pClientData->szHost, " ");
  1928.                 _fstrcat(pClientData->szHost, inet_ntoa(pClientData->inetSock.sin_addr));
  1929.                 _fstrcat(pClientData->szHost, ")"); 
  1930.             }
  1931.         } else {
  1932.             return;                            // What'cha gonna do when it fails on you ?
  1933.         }
  1934.     }
  1935.  
  1936.     /* If this is a face data request packet, hand it off
  1937.        to the face file retriever to to be filled with
  1938.        appropriate data and return it to the requestor. 
  1939.        Note that we do this before attempting to acquire audio
  1940.        output.  This means that half duplex does not prevent our
  1941.        responding to a face request whilst transmitting. */        
  1942.             
  1943.     if ((pClientData->state != Embryonic) &&
  1944.         ((d->compression & (fFaceData | faceRequest)) ==
  1945.             (fFaceData | faceRequest))) {
  1946.         int l;
  1947.             
  1948.         processFaceRequest(d);
  1949.         l = (int) d->buffer.buffer_len; 
  1950.         revlong(&(d->compression));
  1951.         revlong(&(d->buffer.buffer_len));
  1952.         if (!useSendNotSendto) {
  1953.             int stat;
  1954.                 
  1955.             stat = sendto(pClientData->sReply, (LPSTR) d,
  1956.                 l + (sizeof(soundbuf) - BUFL),
  1957.                 0, (LPSOCKADDR) &(pClientData->name), sizeof pClientData->name);
  1958.             if (stat < 0) {
  1959.                 useSendNotSendto = TRUE;
  1960.                 if (hDlgPropeller != NULL) {
  1961.                     SetDlgItemText(hDlgPropeller, IDC_PH_SENDTO, rstring(IDS_T_SEND));
  1962.                 }
  1963.             }
  1964.         }
  1965.         if (useSendNotSendto) {
  1966.             send(pClientData->sReply, (LPSTR) d, l + (sizeof(soundbuf) - BUFL), 0);
  1967.         }
  1968.         propeller(IDC_PH_PACKETS_SENT, ++packetsSent);
  1969.         return;
  1970.     }
  1971.     
  1972.     /*    If this is a face data reply, pass it to the mechanism
  1973.         that's assembling the face in memory.  */
  1974.         
  1975.     if ((d->compression & fFaceData) && (hwndClient != NULL) && (pClientData != NULL)) {
  1976.         processFaceData(hwndClient, pClientData, d);
  1977.         return;
  1978.     }
  1979.         
  1980.     //    Grab the audio device if we don't already have it
  1981.     
  1982.     outputTimeout = 0;                    // Reset release output timeout    
  1983.     if (!obtainOutput(hwnd)) {
  1984.         hdxPacketLost = TRUE;
  1985.         /* If we're broadcasting, we ignore the failure to obtain
  1986.            output due to half duplex hardware.  This allows remote
  1987.            users to blip us to subscribe to the broadcast. */
  1988.         if (!broadcasting) {
  1989.             goto FatalAudioExit;
  1990.         }
  1991.     }
  1992.         
  1993.     if (pClientData == NULL) {
  1994.         goto FatalExit;
  1995.     }
  1996.     
  1997.     if (pClientData->state == Embryonic) {
  1998.     
  1999.         /* If our main window is iconic and we're establishing a
  2000.            new connection, restore the window so the user can see
  2001.            who's connecting.  "Look who's talking!"  */
  2002.            
  2003.         if (IsIconic(hwnd) && lookWho_sTalking) {
  2004.             ShowWindow(hwnd, SW_SHOWNORMAL);
  2005.         }
  2006.         //  Create a new connection child window
  2007.         hwndClient = createNewConnection(pClientData);
  2008.     }
  2009.  
  2010.     if (hwndClient != NULL) {
  2011.     
  2012.         pClientData->cbReceived += d->buffer.buffer_len;
  2013.         if (pClientData->timeout > 0) {
  2014.             pClientData->timeout = 0;
  2015.         }
  2016.     
  2017.         /*    If loopback is requested, bung it right back to the sender
  2018.             remembering, of course, first to clear the loopback bit.  */
  2019.             
  2020.         if (d->compression & fLoopBack) {
  2021.             d->compression ^= fLoopBack;
  2022.             revlong(&(d->compression));                // *sigh*
  2023.             revlong(&(d->buffer.buffer_len));
  2024.             if (!useSendNotSendto) {
  2025.                 int stat;
  2026.                 
  2027.                 stat = sendto(pClientData->sReply, (LPSTR) d, cbRead,
  2028.                     0, (LPSOCKADDR) &(pClientData->name), sizeof pClientData->name);
  2029.                 if (stat < 0) {
  2030.                     useSendNotSendto = TRUE;
  2031.                     if (hDlgPropeller != NULL) {
  2032.                         SetDlgItemText(hDlgPropeller, IDC_PH_SENDTO, rstring(IDS_T_SEND));
  2033.                     }
  2034.                 }
  2035.             }
  2036.             if (useSendNotSendto) {
  2037.                 send(pClientData->sReply, (LPSTR) d, cbRead, 0);
  2038.             }
  2039.             revlong(&(d->compression));
  2040.             revlong(&(d->buffer.buffer_len));
  2041.             propeller(IDC_PH_PACKETS_SENT, ++packetsSent);
  2042.         }
  2043.  
  2044. #ifdef SHOW_RECEIVED_IN_WINDOW        
  2045.         /* It's ugly to scribble directly into the client's
  2046.            window here, but it's enormously faster than sending
  2047.            him a WM_PAINT and forcing him to redraw everything. */
  2048.            
  2049.         if (!IsIconic(hwndClient)) {
  2050.             HDC hdc = GetDC(hwndClient);
  2051.             
  2052.             WinPrintf(hdc, 4, 20, Format(28), pClientData->cbReceived);
  2053.             ReleaseDC(hwndClient, hdc);        
  2054.         }
  2055. #endif
  2056.  
  2057.         /*    If we're broadcasting, allow a site to drop the broadcast
  2058.             by blipping the mike BroadcastUnsubscribe seconds or later
  2059.             after the initial subscription.  (The delay prevents multiple
  2060.             packets from the original subscribe or unsubscribe request
  2061.             from flipping the subscription back and forth.)  */
  2062.  
  2063.         if (broadcasting) {
  2064.             if ((!pClientData->broadcastEnd) &&
  2065.                 ((GetTickCount() - pClientData->broadcastBeginTime) >
  2066.                     (BroadcastUnsubscribe * 1000L))) {
  2067.                 pClientData->broadcastEnd = TRUE;
  2068.                 pClientData->broadcastBeginTime = GetTickCount();
  2069.             } 
  2070.         }
  2071.  
  2072.         /*    If this is a PGP-encrypted session key, determine if
  2073.             we need to decrypt it and put it into effect.  We disable
  2074.             this during a broadcast so users can't bring us to a
  2075.             screeching halt by sending a PGP key request.  */
  2076.             
  2077.         if ((d->compression & fKeyPGP) && !broadcasting) {
  2078.             pgpSetSessionKey(hwndClient, pClientData, d);
  2079.         } else {
  2080.         
  2081.             /*    If the sending host offers us a face image and
  2082.                 we haven't already undertaken its retrieval, start
  2083.                 up the process to fetch it in the background.  */
  2084.                 
  2085.             if (faceShow && (d->compression & fFaceOffer) &&
  2086.                 (pClientData->face_stat == FSinit)) {
  2087.                 connFetchFace(hwndClient, pClientData);
  2088.             }        
  2089.         
  2090.             //    Okay, now we're ready to play the sound buffer
  2091.     
  2092.             if (!hdxPacketLost) {        
  2093.                 pClientData->state = PlayingReceivedAudio;
  2094.                 playSound(hwndClient, pClientData, d, bitsPerSample, samplesPerSecond);
  2095.             }
  2096.         }
  2097.         return;
  2098.     }
  2099.  
  2100.     //  Unable to create connection window
  2101.  
  2102.     if (pClientData != NULL) {
  2103.         GlobalFreePtr(pClientData);
  2104.     }
  2105.  
  2106. FatalExit:
  2107.     if (hwndClient != NULL) {
  2108.         FORWARD_WM_MDIDESTROY(hwndMDIClient, hwndClient, SendMessage);
  2109.     }
  2110.  
  2111. FatalAudioExit:
  2112.     errorBoxUp = FALSE;
  2113. }
  2114.  
  2115. //    MODEMINPUT  --  Finite state machine for processing modem input
  2116.  
  2117. static int modemInputState = 0;
  2118.  
  2119. static void modemInput(HWND hwnd)
  2120. {
  2121.     char modembuf[256];
  2122.     int l;
  2123.     char *cp = modembuf;
  2124.     static LPSTR op;
  2125.     static short len, ilen;
  2126.     static unsigned short bcrc;
  2127.     COMSTAT cs;
  2128.     
  2129.     int err = GetCommError(modemHandle, &cs);
  2130.     if (err != 0) {
  2131.         MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(16), err);
  2132.         errorRant(hwnd);
  2133.     }
  2134.     l = ReadComm(modemHandle, modembuf, sizeof modembuf);
  2135.     err = GetCommError(modemHandle, &cs);
  2136.     if (err != 0) {    
  2137.         MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(17), err);
  2138.         errorRant(hwnd);
  2139.         return;
  2140.     }
  2141.     
  2142.     while (l-- > 0) {
  2143.         char ch = *cp++;
  2144.         
  2145.         switch (modemInputState) {
  2146.             case 0:
  2147.                 if (ch == 1) {
  2148.                     if (modemSb == NULL) {
  2149.                         modemSb = (LPSTR) GlobalAllocPtr(GPTR, sizeof(soundbuf) + 12);
  2150.                         if (modemSb == NULL) {
  2151.                             return;
  2152.                         }
  2153.                     }
  2154.                     modemInputState++;
  2155.                     op = (LPSTR) modemSb;
  2156.                     break;
  2157.                 }
  2158.                 continue;
  2159.                 
  2160.             case 1:
  2161.             case 2:
  2162.             case 3:
  2163.                 if (ch == (modemInputState + 1)) {
  2164.                     modemInputState++;
  2165.                 } else {
  2166.                     modemInputState = 0;
  2167.                     continue;
  2168.                 }
  2169.                 break;
  2170.                 
  2171.             case 4:
  2172.             case 5:
  2173.             case 6:
  2174.                 modemInputState++;
  2175.                 break;
  2176.             
  2177.             case 7:
  2178.                 /* Look at the length specification and its checksum
  2179.                    inverted copy and proceed only if it's plausible. */
  2180.                 len = *((short FAR *) (op - 3));
  2181.                 revshort(&len);
  2182.                 ilen = (((unsigned short) op[-1]) << 8) | ((unsigned char) ch);
  2183.                 if (len < (sizeof(soundbuf) - BUFL) ||
  2184.                     len > sizeof(soundbuf) || len != ~ilen) {
  2185.                     modemInputState = 0;
  2186.                     continue;    
  2187.                 }
  2188.                 ilen = len;
  2189.                 modemInputState++;
  2190.                 break;
  2191.             
  2192.             //    Receiving sound buffer
  2193.                 
  2194.             case 8:
  2195.                 if (--len <= 0) {
  2196.                     modemInputState++;
  2197.                 }
  2198.                 break;
  2199.                 
  2200.             //    Receiving two CRC bytes
  2201.                 
  2202.             case 9:
  2203.             case 10:
  2204.                 modemInputState++;
  2205.                 if (modemInputState < 11) {
  2206.                     break;
  2207.                 }
  2208.                 //    Wheeee!!!  Note fall-through.
  2209.                 
  2210.             //    Complete buffer received
  2211.             
  2212.             case 11:
  2213.                 modemInputState = 0;        // Reset state machine
  2214.                 bcrc = crc(modemSb, ilen + 4 + 2 * sizeof(unsigned short));
  2215.                 if (((bcrc >> 8) != (unsigned char) op[-1]) || ((bcrc & 0xFF) != (unsigned char) ch)) {
  2216.                     /*    CRC error.  Probably should have options in the
  2217.                         modem options dialogue to discard CRC error buffers
  2218.                         or try to play them regardless.  */
  2219.                     MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(18));
  2220.                     errorRant(hwnd);
  2221.                 }
  2222.                 _fmemcpy(&receivedSoundBuffer,
  2223.                     modemSb + 4 + 2 * sizeof(unsigned short), ilen);  
  2224.                 socketInput(hwnd, 0, 0, MODEM_INPUT_EVENT);
  2225.                 continue;
  2226.         }
  2227.         *op++ = ch;
  2228.     }
  2229.     GetCommEventMask(modemHandle, EV_RXCHAR);                    
  2230. }
  2231.  
  2232. //  Frame_WndProc  --  Main frame window procedure
  2233.  
  2234. LRESULT CALLBACK Frame_WndProc(HWND hwnd, UINT nMessage, WPARAM wParam,
  2235.                                LPARAM lParam)
  2236. {
  2237.     int i;
  2238.     char conn[12];
  2239.     char *cg;
  2240.     
  2241.     switch (nMessage) {
  2242.     
  2243.         HANDLE_MSG(hwnd, WM_COMMAND, onCommand);
  2244.         HANDLE_MSG(hwnd, WM_CREATE, onCreate);
  2245.         HANDLE_MSG(hwnd, WM_SOCKET_SELECT, socketInput);
  2246.         
  2247.         case WM_COMMNOTIFY:
  2248.             if (modemHandle != -1) {
  2249.                 switch (LOWORD(lParam)) {
  2250.                     case CN_RECEIVE:
  2251.                         modemInput(hwnd);
  2252.                         break;
  2253.                         
  2254.                     case CN_TRANSMIT:
  2255.                         GetCommEventMask(modemHandle, EV_TXEMPTY);
  2256.                         break;
  2257.                         
  2258.                     case CN_EVENT:
  2259.                         {
  2260.                             UINT stat = GetCommEventMask(modemHandle, CN_EVENT);
  2261.                             COMSTAT lps;
  2262.                             int err;
  2263.                             
  2264.                             if (stat == EV_ERR) {
  2265.                                 err = GetCommError(modemHandle, &lps);
  2266. //                                if (err & (CE_RXOVER | CE_OVERRUN)) { 
  2267. //                                    FlushComm(modemHandle, 1);
  2268. //                                }
  2269. //                                FlushComm(modemHandle, 0);
  2270.     /*                            
  2271.                                 if (err == CE_OVERRUN) {
  2272.                                     return 0;                // Nothing we can do about it!
  2273.                                 }
  2274.     */                            
  2275.                                 if (err != 0) {
  2276.                                     MsgBox(NULL, MB_ICONSTOP | MB_OK,
  2277.                                             Format(19), err);
  2278.                                     errorRant(hwnd);
  2279.                                 }
  2280.                             }
  2281.                             stat = 0;
  2282.                         }
  2283.                         return 0;
  2284.                 }
  2285.             } 
  2286.             break;
  2287.             
  2288.         case WM_CLOSE:
  2289.             terminateWaveInput();        // Terminate wave input if active
  2290.             if (outputActive) {
  2291.                 V waveOutReset(hWaveOut);
  2292.                 V waveOutClose(hWaveOut);
  2293.                 outputActive = FALSE;
  2294.             }
  2295.             DestroyWindow(hwnd);
  2296.             return 0;    
  2297.         
  2298.         case WM_DESTROY:
  2299.             KillTimer(hwnd, FRAME_TIMER_ID); // Kill main timeout timer
  2300.             if (pfnPropeller != NULL) {
  2301.                 FreeProcInstance(pfnPropeller); // Release propeller-head procedure
  2302.             }
  2303.             if (pfnAnswer != NULL) {
  2304.                 FreeProcInstance(pfnAnswer);// Release answering machine procedure
  2305.             }
  2306.             gsm_destroy(gsmh);            // Shut down GSM decoding
  2307.             terminateWaveInput();        // Terminate wave input if active
  2308.             answerClose();                // Close the answering machine
  2309.             closeFaceFile();            // Close face image file
  2310.             closeModem(hwnd);            // Modem Î± dodo
  2311.             if (modemSb != NULL) {
  2312.                 GlobalFreePtr(modemSb);    // Release modem buffer
  2313.             }
  2314.             waveOutShutdown();            // Close wave audio output
  2315.             multicastJoin(NULL, FALSE);    // Drop all multicast group memberships
  2316.             
  2317.             //    If an incomplete LWL periodic update is pending, terminate it
  2318.             
  2319.             if (lwlsock != INVALID_SOCKET) {
  2320.                 shutdown(lwlsock, 2);
  2321.                 closesocket(lwlsock);
  2322.             }
  2323.             if (lwl_t_published) {
  2324.                 sendLwlMessage(hwnd, TRUE);    // Send BYE message to Look Who's Listening
  2325.             }
  2326.             SetCursor(NULL);            // Make sure we don't destroy current cursor
  2327.             if (phoneCursor != NULL) {    
  2328.                 DestroyCursor(phoneCursor);    // Release custom cursors
  2329.             }
  2330.             if (earCursor != NULL) {
  2331.                 DestroyCursor(earCursor);
  2332.             }
  2333.             
  2334.             { 
  2335.                 char pfn[80];
  2336.                 
  2337.                 _fstrcpy(pfn, rstring(IDS_PF_PROFILE_FILE));
  2338.                 
  2339.                 //    Save remembered connections
  2340.                 
  2341.                 wsprintf(conn, Format(5), rememberedConnections);
  2342.                 cg = rstring(IDS_PF_CONNECTIONS); 
  2343.                 WritePrivateProfileString(cg, rstring(IDS_PF_CONNCOUNT),
  2344.                     conn, pfn); 
  2345.                 for (i = 0; i < rememberedConnections; i++) {
  2346.                     
  2347.                     wsprintf(conn, Format(2), i + 1);
  2348.                     WritePrivateProfileString(cg, conn,
  2349.                         rememberedConnection[i], pfn); 
  2350.                     GlobalFreePtr(rememberedConnection[i]);
  2351.                 }
  2352.                 
  2353.                 //    Save multicast group memberships
  2354.                 
  2355.                 wsprintf(conn, Format(5), multiMemberships);
  2356.                 /*    Must re-fetch IDS_PF_MULTIGROUPS each time since there can be
  2357.                     sufficient multicast subscriptions to wrap rstring() circular
  2358.                     buffer. */
  2359.                 WritePrivateProfileString(rstring(IDS_PF_MULTIGROUPS),
  2360.                     rstring(IDS_PF_CONNCOUNT), conn, pfn);
  2361.                 for (i = 0; i < multiMemberships; i++) {
  2362.                     
  2363.                     wsprintf(conn, Format(3), i + 1);
  2364.                     WritePrivateProfileString(rstring(IDS_PF_MULTIGROUPS), conn,
  2365.                         inet_ntoa(multiAddr[i]), pfn); 
  2366.                     wsprintf(conn, Format(4), i + 1);
  2367.                     WritePrivateProfileString(rstring(IDS_PF_MULTIGROUPS), conn,
  2368.                         multiName[i] == NULL ? "" : multiName[i],
  2369.                         pfn);
  2370.                     if (multiName[i] != NULL) { 
  2371.                         GlobalFreePtr(multiName[i]);
  2372.                     }
  2373.                 }
  2374.                 WritePrivateProfileString(rstring(IDS_PF_MULTIGROUPS), rstring(IDS_PF_LOOPBACK),
  2375.                     multiLoop ? kS1 : kS0, pfn);
  2376.                     
  2377.                 //    Save compression modes
  2378.                 
  2379. #define BoolProfile(var, section, itemname, defval) WritePrivateProfileString(section, itemname, var ? kS1 : kS0, pfn)     
  2380.                 cg = rstring(IDS_PF_COMPRESSION);
  2381.                 BoolProfile(compression, cg, rstring(IDS_PF_COMP_SIMPLE), FALSE);
  2382.                 BoolProfile(gsmcompress, cg, rstring(IDS_PF_COMP_GSM), TRUE);
  2383.                 BoolProfile(adpcmcompress, cg, rstring(IDS_PF_COMP_ADPCM), FALSE);
  2384.                 BoolProfile(lpccompress, cg, rstring(IDS_PF_COMP_LPC), FALSE);
  2385.                 
  2386.                 //    Save modem configuration
  2387.                 
  2388.                 cg = rstring(IDS_PF_MODEM);
  2389.                 BoolProfile(modemEnable, cg, rstring(IDS_PF_ENABLE), FALSE);
  2390.                 WritePrivateProfileString(cg, rstring(IDS_PF_PORT),
  2391.                     commport, pfn); 
  2392.                 WritePrivateProfileString(cg, rstring(IDS_PF_BAUDRATE),
  2393.                     baudrate, pfn); 
  2394.                 WritePrivateProfileString(cg, rstring(IDS_PF_MODEM_INIT_STRING),
  2395.                     modemInitString, pfn); 
  2396.                 BoolProfile(modemShowRant, cg, rstring(IDS_PF_SHOW_RANT), TRUE);
  2397.                 
  2398.                 //    Save answering machine settings
  2399.                 
  2400.                 cg = rstring(IDS_PF_ANSWER);
  2401.                 WritePrivateProfileString(cg, rstring(IDS_PF_FILE_NAME),
  2402.                         answerFileName, pfn);
  2403.                    BoolProfile(answerRecord, cg, rstring(IDS_PF_RECORD_MESSAGES), FALSE);            
  2404.        
  2405.                 //    Restore in your face settings
  2406.                 
  2407.                 WritePrivateProfileString(rstring(IDS_PF_FACE), rstring(IDS_PF_FILE_NAME),
  2408.                         faceFileName, pfn);
  2409.                    BoolProfile(faceShow, rstring(IDS_PF_FACE), rstring(IDS_PF_SHOW_FACES), TRUE);    
  2410.                 
  2411.                 //    Save preferences
  2412.     
  2413.                 WritePrivateProfileString(rstring(IDS_PF_RING),
  2414.                     rstring(IDS_PF_FILE_NAME), ringFileName, pfn);
  2415.                 BoolProfile(lookWho_sTalking, rstring(IDS_PF_PREFERENCES), rstring(IDS_PF_LOOKWHO), FALSE);
  2416.  
  2417.                 //    Save Look Who's Listening parameters
  2418.                 
  2419.                 WritePrivateProfileString(rstring(IDS_PF_LWL_TELL),
  2420.                     rstring(IDS_PF_SERVER), lwl_s_server, pfn);
  2421.                 WritePrivateProfileString(rstring(IDS_PF_LWL_TELL),
  2422.                     rstring(IDS_PF_EMAIL), lwl_s_email, pfn);
  2423.                 WritePrivateProfileString(rstring(IDS_PF_LWL_TELL),
  2424.                     rstring(IDS_PF_FULLNAME), lwl_s_fullname, pfn);
  2425.                 WritePrivateProfileString(rstring(IDS_PF_LWL_TELL),
  2426.                     rstring(IDS_PF_PHONE), lwl_s_phone, pfn);
  2427.                 WritePrivateProfileString(rstring(IDS_PF_LWL_TELL),
  2428.                     rstring(IDS_PF_LOCATION), lwl_s_location, pfn);
  2429.                 BoolProfile(lwl_s_publish, rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_PUBLISH), FALSE);
  2430.                 BoolProfile(lwl_s_exact, rstring(IDS_PF_LWL_TELL), rstring(IDS_PF_EXACT), FALSE);
  2431.             
  2432.                 WritePrivateProfileString(rstring(IDS_PF_LWL_ASK),
  2433.                     rstring(IDS_PF_SERVER), lwl_a_server, pfn);
  2434.                 BoolProfile(lwl_a_exact, rstring(IDS_PF_LWL_ASK), rstring(IDS_PF_EXACT), FALSE);
  2435.                 
  2436.                 //    Save workarounds
  2437.                 
  2438.                 cg = rstring(IDS_PF_WORKAROUNDS);    
  2439.                 BoolProfile(alwaysBindSocket, cg, rstring(IDS_PF_ALWAYS_BIND), FALSE);    
  2440.                 BoolProfile(waNetNoConnect, cg, rstring(IDS_PF_NET_NO_CONNECT), FALSE);    
  2441.                 BoolProfile(waNetUseSend, cg, rstring(IDS_PF_NET_USE_SEND), FALSE);    
  2442.                 BoolProfile(waNetMultiTTLisChar, cg, rstring(IDS_PF_MULTICAST_CHAR_ARG), FALSE);    
  2443.                 BoolProfile(waAudioHalf, cg, rstring(IDS_PF_AUDIO_HALF_DUPLEX), FALSE);    
  2444.                 BoolProfile(waAudio11025, cg, rstring(IDS_PF_AUDIO_SAMPLE_11025), FALSE);    
  2445. #undef BoolProfile
  2446.             }
  2447.             
  2448.             //  Disconnect from the sockets library before terminating
  2449.         
  2450.             WSACleanup();
  2451.             
  2452.             //    If we launched help, shut it down now
  2453.             
  2454.             if (holped) {
  2455.                 WinHelp(hwnd, rstring(IDS_HELPFILE), HELP_QUIT, 0L);
  2456.             }
  2457.             PostQuitMessage(0);
  2458.             return 0L;
  2459.             
  2460.         case WM_DROPFILES:
  2461.             {
  2462.                 char dropConn[MAX_PATH];
  2463.                 UINT nfiles = DragQueryFile((HDROP) wParam, 0xFFFF,  dropConn, sizeof dropConn),
  2464.                      fn; 
  2465.                 
  2466.                 for (fn = 0; fn < nfiles; fn++) {
  2467.                     DragQueryFile((HDROP) wParam, fn, dropConn, sizeof dropConn);
  2468.                     newConnection(hwnd, dropConn, NULL);
  2469.                 }
  2470.                 DragFinish((HDROP) wParam);
  2471.             }
  2472.             break;
  2473.  
  2474. #ifdef ZZZ
  2475.          case WM_QUERYNEWPALETTE:
  2476.                {
  2477.                  HWND hActive;
  2478.  
  2479.                  hActive = (HWND) LOWORD(SendMessage(hwndMDIClient, WM_MDIGETACTIVE, 0, 0L));
  2480.  
  2481.                  if (hActive != NULL) {
  2482.                     return SendMessage(hActive, WM_QUERYNEWPALETTE, hwnd, 0L);
  2483.                 }
  2484.  
  2485.                  return FALSE;
  2486.              }
  2487. #endif             
  2488.        
  2489.         case WM_INITMENU:
  2490.             {    HMENU menu = (HMENU) wParam;
  2491.                 int connActive = FALSE;
  2492.                 HWND hWndClient = getActiveConnection();
  2493.                 LPCLIENT_DATA pClientData;
  2494.                 HMENU connect;
  2495.                 
  2496.                 /*    Tack recent remembered connections to the end of
  2497.                     the "Connect" menu.  */
  2498.                 
  2499.                 connect = GetSubMenu(GetMenu(hwnd), 0);
  2500.                 if (connect != NULL) {
  2501.                     int i;
  2502.                     
  2503.                     for (i = 0; i <= REMEMBER_CONNECTIONS; i++) {
  2504.                         DeleteMenu(connect, IDM_CUSTOM + i, MF_BYCOMMAND);
  2505.                     }
  2506.                     if (rememberedConnections > 0) {
  2507.                         AppendMenu(connect, MF_SEPARATOR, IDM_CUSTOM, NULL); 
  2508.                         for (i = 0; i < rememberedConnections; i++) {
  2509.                             AppendMenu(connect, MF_STRING, IDM_CUSTOM + i + 1,
  2510.                                 rememberedConnection[i]);    
  2511.                         } 
  2512.                     }
  2513.                 }
  2514.                 if (hWndClient != NULL) {
  2515.                     connActive = TRUE;
  2516.                     pClientData = CLIENTPTR(hWndClient);
  2517.                 }
  2518. #define Enable(item, condition) EnableMenuItem(menu, (item), MF_BYCOMMAND | \
  2519.             ((condition) ? MF_ENABLED : (MF_DISABLED | MF_GRAYED)));
  2520. #define Checker(item, value) CheckMenuItem(menu, (item), MF_BYCOMMAND | \
  2521.             ((value) ? MF_CHECKED : MF_UNCHECKED))             
  2522.             
  2523.                 Enable(IDM_CONN_PROPERTIES, connActive);
  2524.                 Enable(IDM_CONN_RING, connActive && pClientData->hFile == HFILE_ERROR &&
  2525.                     pClientData->mmioHandle == NULL);
  2526.                 Enable(IDM_SEND_SOUND_FILE, connActive && pClientData->hFile == HFILE_ERROR &&
  2527.                     pClientData->mmioHandle == NULL);
  2528.                 Enable(IDM_DISCONNECT, connActive);
  2529.                 Enable(IDM_CONN_SAVE, connActive && pClientData->connectionFileName[0]);
  2530.                 Enable(IDM_CONN_SAVE_AS, connActive);
  2531.                 Enable(IDM_CO_RECORD, answerEnabled());
  2532.                 Checker(IDM_CO_RECORD, answerEnabled() && answerRecord);
  2533.                 Checker(IDM_CONN_BROADCAST, broadcasting);
  2534.                 Checker(IDM_OPT_LOOK_WHO, lookWho_sTalking);
  2535.                 
  2536.                 //    Workarounds can be changed only when no connection active
  2537.  
  2538. #define WorkA(item, value) Enable(item, !connActive); Checker(item, value)                
  2539.                 WorkA(IDM_WORKA_BIND, alwaysBindSocket);
  2540.                 WorkA(IDM_WORKA_NOCONNECT, waNetNoConnect);
  2541.                 WorkA(IDM_WORKA_USE_SEND, waNetUseSend);
  2542.                 WorkA(IDM_WORKA_TTLCHAR, waNetMultiTTLisChar);
  2543.                 WorkA(IDM_WORKA_HALF_DUPLEX, waAudioHalf);
  2544.                 WorkA(IDM_WORKA_SAMPLE_11025, waAudio11025);
  2545. #undef WorkA                
  2546.                 
  2547.                 //    Compression can be changed only when input is idle
  2548.                 
  2549.                 Enable(IDM_COMP_2X, !inputActive);
  2550.                  Checker(IDM_COMP_2X, compression);
  2551.                 Enable(IDM_COMP_GSM, !inputActive);
  2552.                  Checker(IDM_COMP_GSM, gsmcompress);
  2553.                 Enable(IDM_COMP_ADPCM, !inputActive);
  2554.                  Checker(IDM_COMP_ADPCM, adpcmcompress);
  2555.                 Enable(IDM_COMP_LPC, !inputActive);
  2556.                  Checker(IDM_COMP_LPC, lpccompress);
  2557.                      
  2558.                 //    Audio must be idle to change 8/16 bit mode
  2559.                 
  2560.                 Enable(IDM_OPT_8BIT, !audioIs8Bit && !inputActive && !outputActive);
  2561.                  Checker(IDM_OPT_8BIT, (audioUse8Bit || audioIs8Bit));
  2562.                  
  2563.                  /*    Can't access LWL server when input active (because it
  2564.                      could lead to a reentrant call to WINSOCK.  */
  2565.                      
  2566.                  Enable(IDM_DIR_LISTING, !inputActive);
  2567.                  Enable(IDM_DIR_SEARCH, !inputActive);
  2568.                      
  2569.                  //    Can't change modem configuration while modem session active
  2570.                  
  2571.                  Enable(IDM_OPT_MODEM, modemSessions == 0);
  2572.                  if (connActive) {
  2573.                      ModifyMenu(GetSubMenu(menu, 0), IDM_SEND_SOUND_FILE,
  2574.                          MF_BYCOMMAND | MF_STRING | MF_ENABLED, IDM_SEND_SOUND_FILE,
  2575.                          (pClientData->hFile == HFILE_ERROR &&
  2576.                              pClientData->mmioHandle == NULL) ? rstring(IDS_T_SEND_SOUND_MENU) :
  2577.                              rstring(IDS_T_STOP_SOUND_MENU));
  2578.                  }
  2579.                  Enable(IDM_PROPELLER_HEAD, hDlgPropeller == NULL);       
  2580.                  Enable(IDM_CO_REPONDEUR, hDlgAnswer == NULL);       
  2581.             }
  2582. #undef Checker            
  2583. #undef Enable            
  2584.             break;
  2585.             
  2586.         case WM_SOCKET_LWL:
  2587.             if (WSAGETSELECTEVENT(lParam) == FD_WRITE) {
  2588.                 if (send(lwlsock, (char *) sdes, sdesl, 0) < 0) {
  2589.                     int serr = WSAGetLastError();
  2590.                                                 
  2591.                     MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(56),
  2592.                             serr, SockerrToString(serr));
  2593.                     lwl_t_published = FALSE;
  2594.                 }
  2595.                 closesocket(lwlsock);
  2596.                 lwlsock = INVALID_SOCKET;
  2597.             }
  2598.             break;
  2599.  
  2600.         case WM_TIMER:
  2601.             {
  2602.                 HWND hwndChild;
  2603.             
  2604.                 hwndChild = GetWindow(hwndMDIClient, GW_CHILD);
  2605.             
  2606.                 while (hwndChild != NULL) {
  2607.                     if (GetWindow(hwndChild, GW_OWNER) == NULL) {
  2608.                         FORWARD_WM_TIMER(hwndChild, wParam, PostMessage);
  2609.                     }
  2610.             
  2611.                     hwndChild = GetWindow(hwndChild, GW_HWNDNEXT);
  2612.                 }
  2613.                 
  2614.                 /* Now this is thoroughly idiotic place to initialise
  2615.                    the modem at start-up time, isn't it?   Why do it
  2616.                    here?  Because if you do it at the logical place, in
  2617.                    WM_CREATE, it doesn't work.  No error return anywhere
  2618.                    along the way, of course--this is Windows, after all,
  2619.                    but the port is as dead as the weight of Microsoft
  2620.                    on the struggling developer. */
  2621.                 
  2622.                 if (modemEnable && modemHandle == -1) {
  2623.                     if (!openModem(hwnd)) {
  2624.                             modemEnable = FALSE;
  2625.                     }
  2626.                 }
  2627.             }
  2628.             
  2629.             /*    If TIMEOUT_AUDIO_OUTPUT seconds have elapsed since we
  2630.                 sent the last buffer to audio output, shut down audio
  2631.                 output so as to make it available to other applications.  */
  2632.                 
  2633.             if (outputActive && outputPending == 0 &&
  2634.                 ++outputTimeout >= TIMEOUT_AUDIO_OUTPUT) {
  2635.                 answerSync();
  2636.                 waveOutShutdown();    
  2637.             }
  2638.             
  2639.             /*    If we're publishing Look Who's Listening information and
  2640.                 we're approaching the timeout interval for the server,
  2641.                 re-send our information to avoid being timed out.  */
  2642.             
  2643.             if (lwl_t_published && !lwl_t_diactive) {
  2644.                 if (--lwl_t_resend <= 0) {
  2645.                     lwl_t_resend = TIMEOUT_RESEND_LWL;    // Reset resend timer
  2646. #ifdef BLOCK_SOCK                    
  2647.                     if ((lwl_t_published = sendLwlMessage(hwnd, FALSE)) != FALSE) {
  2648.                         lwl_t_resend = TIMEOUT_RESEND_LWL;    // Set resend timer
  2649.                     }
  2650. #else 
  2651.  
  2652.                     /* We want to do this update in asynchronous mode so
  2653.                        as not to interfere with sound traffic.  Create the
  2654.                        socket, place it into async mode, and initiate the
  2655.                        connection.  We'll proceed with the subsequent steps
  2656.                        when we receive notification of the connection.
  2657.                        
  2658.                        First of all, we want to be sure the last periodic
  2659.                        update isn't still pending.  If so, that indicates
  2660.                        things are hung up, so we'll disable LWL for the
  2661.                        test of this session. */
  2662.                        
  2663.                     if (lwlsock != INVALID_SOCKET) {
  2664.                         shutdown(lwlsock, 2);
  2665.                         closesocket(lwlsock);
  2666.                         lwlsock = INVALID_SOCKET;
  2667.                         lwl_t_published = FALSE;
  2668.                         MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(58));
  2669.                     } else {
  2670.                         lwlsock = socket(AF_INET, SOCK_STREAM, 0);
  2671.                         if (lwlsock == INVALID_SOCKET) {
  2672.                             int serr = WSAGetLastError();
  2673.                                                         
  2674.                             MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(54),
  2675.                                     serr, SockerrToString(serr));
  2676.                             lwl_t_published = FALSE;
  2677.                         } else {
  2678.                             int ling = TRUE;
  2679.                             
  2680.                             WSAAsyncSelect(lwlsock, hwnd, WM_SOCKET_LWL, FD_WRITE | FD_CLOSE);  
  2681.                             setsockopt(lwlsock, SOL_SOCKET, SO_DONTLINGER, (char *) &ling, sizeof ling);
  2682.                             if (connect(lwlsock, (struct sockaddr *) &(lookhost), sizeof lookhost) == SOCKET_ERROR) {
  2683.                                 int serr = WSAGetLastError();
  2684.                                 
  2685.                                 if (serr != WSAEWOULDBLOCK) {                                
  2686.                                     MsgBox(hwnd, MB_ICONSTOP | MB_OK, Format(56),
  2687.                                             serr, SockerrToString(serr));
  2688.                                     closesocket(lwlsock);
  2689.                                     lwlsock = INVALID_SOCKET;
  2690.                                     lwl_t_published = FALSE;
  2691.                                 }
  2692.                             }
  2693.                         }
  2694.                     }
  2695. #endif                    
  2696.                 }                        
  2697.             }     
  2698.             break;
  2699.             
  2700.         /*    Input sound buffer received.  Pass it on to the output
  2701.             handler of every client window that presently says it
  2702.             wants input.  */
  2703.         
  2704.         case MM_WIM_DATA:
  2705.             {
  2706.                 LPWAVEHDR isb;
  2707.                 HWND hwndClient = GetWindow(hwndMDIClient, GW_CHILD);
  2708.             
  2709.                 if (!inputTerm) {
  2710.                     int firstTime = TRUE;
  2711.                     
  2712.                     /*    One little subtlety.  Since we allow the user to
  2713.                         change compression modes on the fly, the buffer that
  2714.                         just arrived may have been filled based on out of
  2715.                         date compression modes.  In particular, if it's longer
  2716.                         than we currently expect, it could frag the network.
  2717.                         If this is the case, chop the extra samples off the
  2718.                         end of the buffer.  This causes momentary loss of sound,
  2719.                         but that's a lot better than the dreaded "Cannot write to
  2720.                         socket" network error.  There's no problem processing a
  2721.                         buffer shorter than expected, except a possible momentary
  2722.                         click in GSM encoding. */
  2723.                         
  2724.                     isb = (LPWAVEHDR) lParam;
  2725.                     if (isb->dwBytesRecorded > (DWORD) currentInputLength) {
  2726.                         isb->dwBytesRecorded = currentInputLength;
  2727.                     }
  2728.                             
  2729.                     while (hwndClient != NULL) {
  2730.                         if ((WNDPROC) GetWindowLong(hwndClient, GWL_WNDPROC) == ((WNDPROC) connectWndProc)) {
  2731.                             LPCLIENT_DATA pClientData = CLIENTPTR(hwndClient);
  2732.                         
  2733.                             if ((pClientData != NULL) &&
  2734.                                 IS_CLIENT_WINDOW(hwndClient) &&
  2735.                                 (pClientData->wantsInput || broadcasting)) {
  2736.                     
  2737. #ifdef SHOW_MIM
  2738. if (!IsIconic(hwnd)) {
  2739.     HDC hdc = GetDC(hwnd);
  2740.     static long reccount = 0;
  2741.                                     
  2742.     reccount += isb->dwBytesRecorded;
  2743.     WinPrintf(hdc, 6, 1, "Input: %lu", reccount);
  2744.     ReleaseDC(hwnd, hdc);        
  2745. }
  2746. #endif                          
  2747.                                 if (firstTime) {
  2748.                                     createSoundBuffer(isb->lpData,
  2749.                                         (WORD) isb->dwBytesRecorded,
  2750.                                         audioChannels, samplesPerSecond,
  2751.                                         bytesPerSecond, sampleAlignment);
  2752.                                     firstTime = FALSE;
  2753.                                 }
  2754.                                 shipSoundBuffer(hwndClient, pClientData);
  2755.                             }
  2756.                         }
  2757.                         hwndClient = GetWindow(hwndClient, GW_HWNDNEXT);
  2758.                     }
  2759.                     isb->dwBufferLength = currentInputLength;                    
  2760.                     waveInAddBuffer(hWaveIn, isb, sizeof(WAVEHDR));
  2761.                 }
  2762.             }
  2763.             break;
  2764.         
  2765.         //    Output sound buffer complete
  2766.         
  2767.         case MM_WOM_DONE:
  2768.             {
  2769.                 LPWAVEHDR waveHdr = (LPWAVEHDR) lParam;
  2770.                 outputPending--;
  2771.                 if (hDlgPropeller != NULL) {
  2772.                     char s[80];
  2773.                     
  2774.                     wsprintf(s, outputPending == 0 ? Format(6) : Format(7),
  2775.                         outputPending);
  2776.                     SetDlgItemText(hDlgPropeller, IDC_PH_AUDIO_OUT_QUEUE, s);
  2777.                 }
  2778. #ifdef DEBUG_OUTPUT_RELEASE
  2779. if (!IsIconic(hwnd)) {
  2780.     HDC hdc = GetDC(hwnd);
  2781.     static long bf = 0;
  2782.                     
  2783.     bf += waveHdr->dwBufferLength / 2;    
  2784.     WinPrintf(hdc, 3, 30, " Freed: %lu  Pending: %lu", bf, outputPending);
  2785.     ReleaseDC(hwnd, hdc);        
  2786. }
  2787. #endif
  2788.                 waveOutUnprepareHeader(hWaveOut, waveHdr, sizeof(WAVEHDR));
  2789.                 GlobalFreePtr(waveHdr->lpData);
  2790.                 GlobalFreePtr(waveHdr);
  2791.                 if ((halfDuplexTransition || outputInShutdown) &&
  2792.                         (outputPending == 0)) {
  2793.                     waveOutClose(hWaveOut);
  2794.                     outputActive = FALSE;
  2795.                     if (!outputInShutdown && halfDuplexTransition) {
  2796.                         V startWaveInput(hwnd);
  2797.                     }
  2798.                     halfDuplexTransition = outputInShutdown = FALSE;
  2799.                     propUpdateAudio();
  2800.                 }
  2801.             }                         
  2802.             break;
  2803.             
  2804.         default:
  2805.         
  2806.             //    Check for help button in a file open/save dialogue we own
  2807.         
  2808.             if (nMessage == fileOpenHelpButton && fileHelpKey != NULL) {
  2809.                 WinHelp(hwnd, rstring(IDS_HELPFILE), HELP_KEY,
  2810.                             ((DWORD) (LPSTR) fileHelpKey));
  2811.                 holped = TRUE;
  2812.             }
  2813.             break;      
  2814.     }                               
  2815.  
  2816.     return DefFrameProc(hwnd, hwndMDIClient, nMessage, wParam, lParam);
  2817.  
  2818. }
  2819.  
  2820.